package com.hmkj.core.service.order.impl;

import com.alibaba.fastjson.JSON;
import com.hmkj.common.baseMapper.GenericMapper;
import com.hmkj.common.baseMapper.GenericPo;
import com.hmkj.common.utils.BigDecimalUtils;
import com.hmkj.common.utils.OrderNoUtils;
import com.hmkj.common.utils.StringUtils;
import com.hmkj.common.utils.ValidateUtils;
import com.hmkj.core.api.ufx.call.TransactionCall;
import com.hmkj.core.api.ufx.model.req.EntrustNativeReq;
import com.hmkj.core.api.ufx.model.req.EntrustOtcReq;
import com.hmkj.core.api.ufx.model.req.EntrustWithdrawReq;
import com.hmkj.core.api.ufx.model.res.DealPush;
import com.hmkj.core.api.ufx.model.res.EntrustPush;
import com.hmkj.core.api.ufx.model.res.EntrustRes;
import com.hmkj.core.api.ufx.model.res.LoginRes;
import com.hmkj.core.constant.*;
import com.hmkj.core.exception.BussinessException;
import com.hmkj.core.exception.UFXLoginException;
import com.hmkj.core.mapper.amount.AccountLogMapper;
import com.hmkj.core.mapper.amount.AccountMapper;
import com.hmkj.core.mapper.amount.PointLogMapper;
import com.hmkj.core.mapper.commo.CommoMapper;
import com.hmkj.core.mapper.investor.InvestorMapper;
import com.hmkj.core.mapper.operate.AgentMapper;
import com.hmkj.core.mapper.order.OrderMapper;
import com.hmkj.core.mapper.user.UserMapper;
import com.hmkj.core.model.order.OrderModel;
import com.hmkj.core.model.quote.RealTimeData;
import com.hmkj.core.po.amount.Account;
import com.hmkj.core.po.amount.AccountLog;
import com.hmkj.core.po.commo.Commo;
import com.hmkj.core.po.investor.Investor;
import com.hmkj.core.po.operate.Agent;
import com.hmkj.core.po.order.Order;
import com.hmkj.core.po.user.PointLog;
import com.hmkj.core.po.user.User;
import com.hmkj.core.service.cache.RedisService;
import com.hmkj.core.service.operate.PointRuleService;
import com.hmkj.core.service.order.OrderService;
import com.hmkj.core.service.risk.RiskParamService;
import com.xiaoleilu.hutool.util.RandomUtil;
import com.xiaoleilu.hutool.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 策略表业务相关Service接口实现<br>
 *
 * @author eproo
 * @ClassName: OrderServiceImpl
 * @date 2017-11-07 06:58:23
 */
@Slf4j
@Service
@Scope("prototype")
@Transactional(rollbackFor = Exception.class)
public class OrderServiceImpl implements OrderService {

    @Resource
    private OrderMapper orderMapper;
    @Resource
    private UserMapper userMapper;
    @Resource
    private AccountMapper accountMapper;
    @Resource
    private AccountLogMapper accountLogMapper;
    @Resource
    private CommoMapper commoMapper;
    @Resource
    private RedisService redisService;
    @Resource
    private RiskParamService riskParamService;
    @Resource
    private AgentMapper agentMapper;
    @Resource
    private InvestorMapper investorMapper;
    @Resource
    private PointRuleService pointRuleService;
    @Resource
    private PointLogMapper pointLogMapper;

    @Override
    public GenericMapper<Order, Integer> _getMapper() {
        return orderMapper;
    }

    @Override
    public void takingOrder(Order order, Integer investorId) {
        Investor investor = investorMapper.selectByPrimaryKey(investorId);
        //后台已知投资人接单
        handTradeEntrust(order, investor);
    }

    @Override
    public void flowOrder(Order order) {

        //更改订单状态（已流单）
        int result4 = orderMapper.updateByPrimaryKeySelective(new Order(t -> {
            t.setId(order.getId());
            t.setStatus(Order.STATUS.T8.code);
        }));
        //点买人的账户
        Account userAccount = accountMapper.selectByUID(order.getUserId());
        // 手续费
        BigDecimal totalFee = order.getFee().multiply(new BigDecimal(order.getNumber())).setScale(2, BigDecimal.ROUND_HALF_UP);
        //实际手续费（手续费-抵扣积分）
        BigDecimal realTotalFee = totalFee.subtract(new BigDecimal(order.getUsePoint()));
        //增加点买人资金记录
        int m1 = accountLogMapper.insert(AccountLog.buildLog(order.getUserId(), order.getDeposit(), AccountLog.TYPE.T19, userAccount, "流单成功，解冻履约保证金" + order.getDeposit() + "元。订单ID：" + order.getId()));
        if (m1 != 1) throw new BussinessException("添加解冻履约保证金资金记录失败");
        int m2 = accountLogMapper.insert(AccountLog.buildLog(userAccount.getUserId(), realTotalFee, AccountLog.TYPE.T16, userAccount, "流单成功，解冻交易综合费" + realTotalFee + "元。订单ID：" + order.getId()));
        if (m2 != 1) throw new BussinessException("添加点买人退还手续费资金记录失败");
        //退还点买人履约保证金，退还点买人手续费，
        int result1 = accountMapper.updateAccount(BigDecimal.ZERO, BigDecimalUtils.add(order.getDeposit(), realTotalFee), BigDecimalUtils.add(order.getDeposit(), realTotalFee).negate(), order.getUserId());
        //退还点买人抵扣积分
        if (order.getUsePoint() > 0) {
            // 退还用户积分
            if (pointLogMapper.insert(PointLog.buildLog(userMapper.selectByPrimaryKey(order.getUserId()), order.getUsePoint(), PointNid.T8, "流单成功，返还使用积分：" + order.getUsePoint() + "。订单号：" + order.getId())) != 1) {
                throw new BussinessException("新增积分退回记录失败");
            }
            userMapper.updatePoint(order.getUsePoint(), order.getUserId());
        }
        if (null != order.getInvestId()) {
            //投资人的模拟账户
            Account investorAccount = accountMapper.selectByUID(order.getInvestId());
            //增加投资人资金记录
            int n1 = accountLogMapper.insert(AccountLog.buildLog(userAccount.getUserId(), order.getFee(), AccountLog.TYPE.T16, investorAccount, "流单成功，解冻止盈保证金" + order.getProfitDeposit() + "元。订单ID：" + order.getId()));
            if (n1 != 1) throw new BussinessException("添加解冻止盈保证金资金记录失败");
            //退还投资人止盈保证金，
            int result3 = accountMapper.updateAccount(BigDecimal.ZERO, order.getProfitDeposit(), BigDecimalUtils.mul(order.getProfitDeposit(), -1), order.getInvestId());
            if (result3 != 1) throw new BussinessException("流单失败");
        }
        if (result1 != 1 || result4 != 1) {
            throw new BussinessException("流单失败");
        }
    }

    @Override
    public void cancelOrder(Integer id, Integer userId, Order.CANCLETYPE cancleType) throws Exception {
        Order order = orderMapper.selectByPrimaryKey(id);
        if (order == null) throw new BussinessException("订单不存在");
        if (Order.STATUS.T2.code != order.getStatus()) throw new BussinessException("该订单当前状态不能进行撤单操作");
        if (!order.getUserId().equals(userId) && !order.getInvestId().equals(userId))
            throw new BussinessException("您无权进行当前操作");

//        if (StrUtil.isNotBlank(order.getOpenEntrNo())) {
//            // 调用易盛撤单接口
//            EntrustRes res = TransactionCall.getInstance().tradeEntrustWithdraw(buildCancleReqParam(order));
//            if (res == null || StrUtil.isBlank(res.getFutuEntrustNo())) throw new BussinessException("撤单失败，接口无返回");
//
//            // 更新订单状态为撤单中
//            orderMapper.updateByIdAndVersionSelective(new Order(t -> {
//                t.setId(order.getId());
//                t.setVersion(order.getVersion());
//                t.setStatus(Order.STATUS.T9.code);
//                t.setCancleEntrNo(res.getFutuEntrustNo());
//            }));
//        } else {
        // 更新订单状态为撤单，并解冻资金
        doOrderCancle(order, Order.STATUS.T10, cancleType);
//        }

        // 限价单测单需要删除缓存中的记录
        if (order.getOpenWay().intValue() == Order.OPENWAY.T2.code) {
            String key = CacheID.LINIT_ORDER + order.getCommoId() + ":" + order.getDirection() + ":" + order.getEntrustPrice().setScale(4).toString();
            redisService.mapDel(key, order.getId().toString());
        }

    }

    private EntrustWithdrawReq buildCancleReqParam(Order order) throws Exception {
        // 获取缓存中的投资人资金账户信息
        Investor investor = investorMapper.selectOne(new Investor(t -> t.setUserId(order.getInvestId())));

        String token = redisService.get(CacheID.INVESTOR + investor.getAccountNo());
        if (StrUtil.isBlank(token)) {
            // 进行登录操作
            log.info("外盘账号登录：{}_{}", investor.getAccountNo(), investor.getAccountPwd());
            LoginRes res = TransactionCall.getInstance().accountLogin(getOpStation(investor.getUserId(), "47.93.207.165"), investor.getAccountNo(), investor.getAccountPwd(), Commo.TYPE.T1.code);
            log.info("易盛登录结果：" + JSON.toJSONString(res));
            if (res == null || StrUtil.isBlank(res.getUserToken()))
                throw new BussinessException("易盛登录失败，返回信息：{}", JSON.toJSONString(res));
            redisService.put(CacheID.INVESTOR + investor.getAccountNo(), res.getUserToken(), 60 * 60 * 1);
        }
        EntrustWithdrawReq req = new EntrustWithdrawReq();
        req.setOpStation("");
        req.setUserToken(token);
        req.setFundAccount(investor.getAccountNo());
        req.setFutuEntrustNo(order.getOpenEntrNo());
        req.setFutuExchType(riskParamService.getParamValue(RiskNID.EXCH_TYPE, order.getCommoId()));
        req.setCommodityType(order.getCode());
        req.setContractCode(order.getContract());
        req.setEntrustReference(EntrustType.CANCLE);
        return req;
    }

    @Override
    public void coverOrder(int orderId, Integer num, Order.SETTLETYPE settleType) throws Exception {

        log.info("开始策略平仓操作，策略ID:{}，平仓数量：{}，平仓类型：{}", orderId, num, settleType.value);

        Order order = orderMapper.selectByPrimaryKey(orderId);

        if (order == null) {
            throw new BussinessException("平仓订单不存在，订单ID:" + orderId);
        }
        if (order.getStatus() != Order.STATUS.T3.code) {
            throw new BussinessException("当前订单状态已更改");
        }
        if (order.getNumber() < num || num <= 0) {
            throw new BussinessException("平仓数量不正确");
        }

        Commo commo = redisService.get(CacheID.COMMO_ID_PREFIX + order.getCommoId(), Commo.class);

        if (order.getNumber().intValue() == num.intValue()) {
            // 全部平仓,调用易盛接口发起平仓
            EntrustRes res = null;
            try {
                Order corder = new Order(t -> {
                    BeanUtils.copyProperties(order, t);
                    t.setNumber(num);
                    if (order.getDirection().equals(Order.DIRECTION.T1.code)) {
                        t.setDirection(Order.DIRECTION.T2.code);
                    } else {
                        t.setDirection(Order.DIRECTION.T1.code);
                    }
                    // 市价单直接匹配投资人进行下单
                    RealTimeData realTimeData = redisService.get(CacheID.QUOTE_REAL_PREFIX + order.getCommoId(), RealTimeData.class);
                    t.setEntrustPrice(getEntrustPrice(commo.getType(), Order.OPENWAY.T1.code, t.getDirection(), BigDecimal.ZERO, realTimeData));
                });

                if (commo.getType().intValue() == Commo.TYPE.T1.code) {
                    res = TransactionCall.getInstance().tradeEntrustOtc(buildOtcTradeParams(corder, EntrustType.SETTLE, "47.93.207.165"));
                } else {
                    res = TransactionCall.getInstance().tradeEntrustNative(buildNativeTradeParams(corder, EntrustType.SETTLE, "47.93.207.165"));
                }
                log.info("平仓下单结果：" + JSON.toJSONString(res));
            } catch (UFXLoginException e) {
                log.error("易盛登录异常：" + e.getMessage());
                e.printStackTrace();
                Investor investor = investorMapper.selectOne(new Investor(t -> t.setUserId(order.getInvestId())));
                // 重新登录
                LoginRes logres;
                if (commo.getType().intValue() == Commo.TYPE.T1.code) {
                    log.info("外盘账号登录：{}_{}", investor.getAccountNo(), investor.getAccountPwd());
                    logres = TransactionCall.getInstance().accountLogin(getOpStation(investor.getUserId(), "47.93.207.165"), investor.getAccountNo(), investor.getAccountPwd(), Commo.TYPE.T1.code);
                } else {
                    log.info("内盘账号登录：{}_{}", investor.getAccountHomeNo(), investor.getAccountHomePwd());
                    logres = TransactionCall.getInstance().accountLogin(getOpStation(investor.getUserId(), "47.93.207.165"), investor.getAccountHomeNo(), investor.getAccountHomePwd(), Commo.TYPE.T2.code);
                }
                log.info("易盛登录结果：" + JSON.toJSONString(logres));
                if (logres != null && StrUtil.isNotBlank(logres.getUserToken())) {
                    redisService.put(CacheID.INVESTOR + investor.getAccountNo(), logres.getUserToken(), 60 * 60 * 1);
                }
                throw new BussinessException("账号登录异常");
            }

            if (res == null || StrUtil.isBlank(res.getFutuEntrustNo())) throw new BussinessException("平仓失败，接口无返回");

            // 修改订单状态为平仓中,更新平仓委托单号
            EntrustRes finalRes = res;
            int c2 = orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order.getId());
                t.setVersion(order.getVersion());
                t.setSettleType(settleType.code);
                t.setStatus(Order.STATUS.T4.code);
                t.setSettleEntrNo(finalRes.getFutuEntrustNo());
            }));
            if (c2 != 1) throw new BussinessException("平仓更新策略平仓委托编号失败");

        } else if (order.getNumber() > num) {
            // 部分平仓，先拆分订单
            // 原订单号
            String orderNo = order.getOrderNo();

            String orderNo1;
            String orderNo2;
            // 计算新订单相关参数 止盈、止损、履约保证金、止盈保证金（按数量比例分配）
            BigDecimal scale = new BigDecimal(num).divide(new BigDecimal(order.getNumber()), 8, BigDecimal.ROUND_HALF_UP);
            BigDecimal deposit = scale.multiply(order.getDeposit()).setScale(2, BigDecimal.ROUND_HALF_UP);
            BigDecimal profitDeposit = scale.multiply(order.getProfitDeposit()).setScale(2, BigDecimal.ROUND_HALF_UP);

            // 拆分订单积分
            Integer usePoint1 = 0;// 原订单积分数量
            Integer usePoint2 = 0;// 新订单积分数量
            if (order.getUsePoint() > 0) {
                usePoint2 = order.getUsePoint() * num / order.getNumber();
                usePoint1 = order.getUsePoint() - usePoint2;
            }
            // 处理订单号
            if (orderNo.contains("S")) {
                // 已拆分过
                orderNo1 = orderNo;
                orderNo2 = orderNo.substring(0, orderNo.indexOf("S")) + "S" + getLastPrefix(orderNo.substring(0, orderNo.indexOf("S")), "S");
            } else {
                // 未拆分过
                orderNo1 = orderNo + "S1";
                orderNo2 = orderNo + "S2";
            }

            // 更新原新单
            Integer finalUsePoint1 = usePoint1;
            if (orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order.getId());
                t.setVersion(order.getVersion());
                t.setOrderNo(orderNo1);
                t.setNumber(order.getNumber() - num);
                t.setDeposit(order.getDeposit().subtract(deposit));
                t.setProfitDeposit(order.getProfitDeposit().subtract(profitDeposit));
                t.setUsePoint(finalUsePoint1);
            })) != 1) throw new BussinessException("更新原订单失败");

            // 新增成交订单
            Integer finalUsePoint2 = usePoint2;
            orderMapper.insert(new Order(t -> {
                BeanUtils.copyProperties(order, t);
                t.setId(null);
                t.setVersion(null);
                t.setOrderNo(orderNo2);
                t.setNumber(num);
                t.setStatus(Order.STATUS.T4.code);
                t.setSettleType(settleType.code);
                t.setDeposit(deposit);
                t.setProfitDeposit(profitDeposit);
                t.setUsePoint(finalUsePoint2);
            }));
            Order order2 = orderMapper.selectOne(new Order(t -> t.setOrderNo(orderNo2)));

            // 调用易盛接口发起平仓
            EntrustRes res = null;
            try {
                Order corder = new Order(t -> {
                    BeanUtils.copyProperties(order2, t);
                    if (order2.getDirection().intValue() == Order.DIRECTION.T1.code) {
                        t.setDirection(Order.DIRECTION.T2.code);
                    } else {
                        t.setDirection(Order.DIRECTION.T1.code);
                    }
                    // 市价单直接匹配投资人进行下单
                    RealTimeData realTimeData = redisService.get(CacheID.QUOTE_REAL_PREFIX + order.getCommoId(), RealTimeData.class);
                    t.setEntrustPrice(getEntrustPrice(commo.getType(), Order.OPENWAY.T1.code, t.getDirection(), BigDecimal.ZERO, realTimeData));
                });

                if (commo.getType().intValue() == Commo.TYPE.T1.code) {
                    res = TransactionCall.getInstance().tradeEntrustOtc(buildOtcTradeParams(corder, EntrustType.SETTLE, "47.93.207.165"));
                } else {
                    res = TransactionCall.getInstance().tradeEntrustNative(buildNativeTradeParams(corder, EntrustType.SETTLE, "47.93.207.165"));
                }


                log.info("平仓下单结果：" + JSON.toJSONString(res));
            } catch (UFXLoginException e) {
                log.error("易盛登录异常：" + e.getMessage());
                e.printStackTrace();
                Investor investor = investorMapper.selectOne(new Investor(t -> t.setUserId(order.getInvestId())));
                // 重新登录
                LoginRes logres;
                if (commo.getType().intValue() == Commo.TYPE.T1.code) {
                    log.info("外盘账号登录：{}_{}", investor.getAccountNo(), investor.getAccountPwd());
                    logres = TransactionCall.getInstance().accountLogin(getOpStation(investor.getUserId(), "47.93.207.165"), investor.getAccountNo(), investor.getAccountPwd(), Commo.TYPE.T1.code);
                } else {
                    log.info("内盘账号登录：{}_{}", investor.getAccountHomeNo(), investor.getAccountHomePwd());
                    logres = TransactionCall.getInstance().accountLogin(getOpStation(investor.getUserId(), "47.93.207.165"), investor.getAccountHomeNo(), investor.getAccountHomePwd(), Commo.TYPE.T2.code);
                }
                log.info("易盛登录结果：" + JSON.toJSONString(logres));
                if (logres != null && StrUtil.isNotBlank(logres.getUserToken())) {
                    redisService.put(CacheID.INVESTOR + investor.getAccountNo(), logres.getUserToken(), 60 * 60 * 1);
                }
                throw new BussinessException("账号登录异常");
            }

            if (res == null || StrUtil.isBlank(res.getFutuEntrustNo())) throw new BussinessException("平仓失败，接口无返回");

            // 更新平仓委托单号
            EntrustRes finalRes = res;
            int c2 = orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order2.getId());
                t.setVersion(order2.getVersion());
                t.setSettleEntrNo(finalRes.getFutuEntrustNo());
            }));
            if (c2 != 1) throw new BussinessException("平仓更新策略平仓委托编号失败");

        } else {
            // 平仓数量错误
            throw new BussinessException("平仓数量有误");
        }
    }

    @Override
    public void coverOrderHandler(Order order, Integer num, BigDecimal settlePrice, Order.SETTLETYPE settleType) throws Exception {
        if (order.getStatus() != Order.STATUS.T3.code) throw new BussinessException("当前订单状态已更改");
        if (order.getNumber() < num || num <= 0) throw new BussinessException("平仓数量不正确");
        if (order == null) throw new BussinessException("订单不存在或已平仓");

        // 每点合约价值
        BigDecimal pointValue = riskParamService.getParamValue(RiskNID.POINT_VALUE, order.getCommoId(), BigDecimal.class);
        // 最小波动点数
        BigDecimal waveMin = riskParamService.getParamValue(RiskNID.WAVE_POINT_MIN, order.getCommoId(), BigDecimal.class);
        // 汇率
        BigDecimal exchangeRate = riskParamService.getParamValue(RiskNID.EXCHANGE_RATE, order.getCommoId(), BigDecimal.class);

        // 计算策略盈亏 = （平仓价-开仓价）* 数量 * 每点合约价 / 最小波动点
        BigDecimal profit = settlePrice.subtract(order.getOpenPrice()).multiply(new BigDecimal(num)).multiply(pointValue).multiply(exchangeRate).divide(waveMin, 2, BigDecimal.ROUND_HALF_UP);
        // 计算盈亏点数  =  （平仓价 - 开仓价）/最小波动点数*手数  取整数
        int profitPoint = settlePrice.subtract(order.getOpenPrice()).multiply(new BigDecimal(num)).divide(waveMin, 2, BigDecimal.ROUND_HALF_UP).intValue();
        if (Order.DIRECTION.T2.code == order.getDirection().intValue()) {
            profit = profit.negate();
            profitPoint = profitPoint * (-1);
        }

        //初始化一个finalOrder用于处理账户资金
        Order finalOrder = null;
        BigDecimal finalProfit = profit;
        int finalProfitPoint = profitPoint;
        // 处理订单信息
        if (num == order.getNumber().intValue()) {
            // 全部平仓，更新订单状态
            int c = orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order.getId());
                t.setVersion(order.getVersion());
                t.setStatus(Order.STATUS.T6.code);
                t.setSettleTime(new Date());
                t.setSettlePrice(settlePrice);
                t.setSettleType(settleType.code);
                t.setSettleBusiNo("HC" + OrderNoUtils.getSerialNumber());
                t.setProfitPoint(finalProfitPoint);
                t.setProfit(finalProfit);
                t.setExRate(exchangeRate);
            }));
            if (c != 1) throw new BussinessException("期货平仓更新订单状态失败");
            finalOrder = order;
        } else if (num < order.getNumber()) {
            // 部分平仓，进行拆单处理
            // 原订单号
            String orderNo = order.getOrderNo();

            String orderNo1;
            String orderNo2;
            // 计算新订单相关参数 履约保证金、止盈保证金（按数量比例分配）
            BigDecimal scale = new BigDecimal(num).divide(new BigDecimal(order.getNumber()), 8, BigDecimal.ROUND_HALF_UP);
            BigDecimal deposit = scale.multiply(order.getDeposit()).setScale(2, BigDecimal.ROUND_HALF_UP);
            BigDecimal profitDeposit = scale.multiply(order.getProfitDeposit()).setScale(2, BigDecimal.ROUND_HALF_UP);
            // 拆分订单积分
            Integer usePoint1 = 0;// 原订单积分数量
            Integer usePoint2 = 0;// 新订单积分数量
            if (order.getUsePoint() > 0) {
                usePoint2 = order.getUsePoint() * num / order.getNumber();
                usePoint1 = order.getUsePoint() - usePoint2;
            }

            // 处理订单号
            if (orderNo.contains("S")) {
                // 已拆分过
                orderNo1 = orderNo;
                orderNo2 = orderNo.substring(0, orderNo.indexOf("S")) + "S" + getLastPrefix(orderNo.substring(0, orderNo.indexOf("S")), "S");
            } else {
                // 未拆分过
                orderNo1 = orderNo + "S1";
                orderNo2 = orderNo + "S2";
            }

            // 更新原新单
            Integer finalUsePoint1 = usePoint1;
            if (orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order.getId());
                t.setVersion(order.getVersion());
                t.setOrderNo(orderNo1);
                t.setNumber(order.getNumber() - num);
                t.setDeposit(order.getDeposit().subtract(deposit));
                t.setProfitDeposit(order.getProfitDeposit().subtract(profitDeposit));
                t.setUsePoint(finalUsePoint1);
            })) != 1) throw new BussinessException("更新原订单失败");

            // 新增成交订单
            BigDecimal finalProfit1 = profit;
            Integer finalUsePoint2 = usePoint2;
            orderMapper.insert(new Order(t -> {
                BeanUtils.copyProperties(order, t);
                t.setId(null);
                t.setVersion(null);
                t.setOrderNo(orderNo2);
                t.setNumber(num);
                t.setStatus(Order.STATUS.T6.code);
                t.setSettleTime(new Date());
                t.setSettlePrice(settlePrice);
                t.setSettleType(settleType.code);
                t.setSettleBusiNo("HC" + OrderNoUtils.getSerialNumber());
                t.setDeposit(deposit);
                t.setProfitDeposit(profitDeposit);
                t.setProfitPoint(finalProfitPoint);
                t.setProfit(finalProfit1);
                t.setUsePoint(finalUsePoint2);
                t.setExRate(exchangeRate);
            }));
            finalOrder = orderMapper.selectOne(new Order(o -> {
                o.setOrderNo(orderNo2);
                o.setDelFlag(Order.DELFLAG.NORMAL.code);
            }));
        } else {
            // 成交数量有误，保存错误数据
            throw new BussinessException("成交数量有误");
        }
        if (null == finalOrder) {
            throw new BussinessException("平仓失败");
        }
        // 处理点买人投资人账户资金
        if (profit.compareTo(BigDecimal.ZERO) == 1) {
            // 计算实际盈利
            BigDecimal profitReal = profit.compareTo(finalOrder.getProfitDeposit()) == 1 ? finalOrder.getProfitDeposit().multiply(finalOrder.getAllotRate()) : profit.multiply(finalOrder.getAllotRate());

            // 处理点买人资金账户
            Account account = accountMapper.selectByUID(finalOrder.getUserId());
            int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), finalOrder.getDeposit(), AccountLog.TYPE.T19, account, "策略盈利，解冻履约保证金" + finalOrder.getDeposit() + "元。订单ID：" + finalOrder.getId()));
            if (c2 != 1) throw new BussinessException("策略盈利，解冻履约保证金失败");
            int c3 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), profitReal, AccountLog.TYPE.T25, account, "策略盈利，转入盈利收入" + profitReal + "元。订单ID：" + finalOrder.getId()));
            if (c3 != 1) throw new BussinessException("策略盈利，转入盈利收入失败");
            int c1 = accountMapper.updateAccount(profitReal, profitReal.add(finalOrder.getDeposit()), finalOrder.getDeposit().negate(), account.getUserId());
            if (c1 != 1) throw new BussinessException("手动平仓成交回报更新点买人资金账户失败");


            // 处理投资人资金账户
            Account ia = accountMapper.selectByUID(finalOrder.getInvestId());
            int ic2 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), finalOrder.getProfitDeposit(), AccountLog.TYPE.T22, ia, "点买人策略盈利，解冻止盈保证金" + finalOrder.getProfitDeposit() + "元。订单ID：" + finalOrder.getId()));
            if (ic2 != 1) throw new BussinessException("点买人策略盈利，解冻止盈保证金失败");
            int ic3 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), profitReal, AccountLog.TYPE.T26, ia, "点买人策略盈利，扣除盈利金额" + profitReal + "元。订单ID：" + finalOrder.getId()));
            if (ic3 != 1) throw new BussinessException("点买人策略盈利，扣除盈利金额失败");
            int ic1 = accountMapper.updateAccount(profitReal.negate(), profitReal.negate().add(finalOrder.getProfitDeposit()), finalOrder.getProfitDeposit().negate(), ia.getUserId());
            if (ic1 != 1) throw new BussinessException("手动平仓成交回报更新点买人资金账户失败");
        } else if (profit.compareTo(BigDecimal.ZERO) == -1) {
            // 计算实际亏损
            BigDecimal loseReal = profit.negate().compareTo(finalOrder.getDeposit()) == 1 ? finalOrder.getDeposit() : profit.negate();

            // 处理点买人资金账户
            Account account = accountMapper.selectByUID(finalOrder.getUserId());
            int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), finalOrder.getDeposit(), AccountLog.TYPE.T19, account, "策略亏损，解冻履约保证金" + finalOrder.getDeposit() + "元。订单ID：" + finalOrder.getId()));
            if (c2 != 1) throw new BussinessException("策略亏损，解冻履约保证金失败");
            int c3 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), loseReal, AccountLog.TYPE.T26, account, "策略亏损，扣除亏损金额" + loseReal + "元。订单ID：" + finalOrder.getId()));
            if (c3 != 1) throw new BussinessException("策略亏损，扣除亏损金额失败");
            int c1 = accountMapper.updateAccount(loseReal.negate(), finalOrder.getDeposit().subtract(loseReal), finalOrder.getDeposit().negate(), account.getUserId());
            if (c1 != 1) throw new BussinessException("手动平仓成交回报更新点买人资金账户失败");


            // 处理投资人资金账户
            Account ia = accountMapper.selectByUID(finalOrder.getInvestId());
            int ic2 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), finalOrder.getProfitDeposit(), AccountLog.TYPE.T22, ia, "点买人策略亏损，解冻止盈保证金" + finalOrder.getProfitDeposit() + "元。订单ID：" + finalOrder.getId()));
            if (ic2 != 1) throw new BussinessException("点买人策略亏损，解冻止盈保证金额失败");
            int ic3 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), loseReal, AccountLog.TYPE.T25, ia, "点买人策略亏损，转入亏损金额" + loseReal + "元。订单ID：" + finalOrder.getId()));
            if (ic3 != 1) throw new BussinessException("点买人策略亏损，转入亏损金额失败");
            int ic1 = accountMapper.updateAccount4Plat(loseReal, loseReal.add(finalOrder.getProfitDeposit()), finalOrder.getProfitDeposit().negate(), ia.getUserId());
            if (ic1 != 1) throw new BussinessException("手动平仓成交回报更新点买人资金账户失败");
        } else {
            // 实际无盈亏

            // 处理点买人资金账户
            Account account = accountMapper.selectByUID(finalOrder.getUserId());
            int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), finalOrder.getDeposit(), AccountLog.TYPE.T19, account, "策略无盈亏，解冻履约保证金" + finalOrder.getDeposit() + "元。订单ID：" + finalOrder.getId()));
            if (c2 != 1) throw new BussinessException("策略无盈亏，解冻履约保证金失败");
            int c1 = accountMapper.updateAccount(BigDecimal.ZERO, finalOrder.getDeposit(), finalOrder.getDeposit().negate(), account.getUserId());
            if (c1 != 1) throw new BussinessException("手动平仓成交回报更新点买人资金账户失败");


            // 处理投资人资金账户
            Account ia = accountMapper.selectByUID(finalOrder.getInvestId());
            int ic2 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), finalOrder.getProfitDeposit(), AccountLog.TYPE.T22, ia, "点买人策略无盈亏，解冻止盈保证金" + finalOrder.getProfitDeposit() + "元。订单ID：" + finalOrder.getId()));
            if (ic2 != 1) throw new BussinessException("点买人策略无盈亏，解冻止盈保证金额失败");
            int ic1 = accountMapper.updateAccount(BigDecimal.ZERO, finalOrder.getProfitDeposit(), finalOrder.getProfitDeposit().negate(), ia.getUserId());
            if (ic1 != 1) throw new BussinessException("手动平仓成交回报更新点买人资金账户失败");
        }
    }

    /**
     * 成交回报
     *
     * @param model 回报参数
     */
    @Override
    public void doDealPush(DealPush model) {
        try {
            if (EntrustType.OPEN.equals(model.getEntrustReference())) {
                // 开仓成交回报
                doOpenDealPush(model);
            } else if (EntrustType.SETTLE.equals(model.getEntrustReference())) {
                // 平仓成交回报
                doCoverDealPush(model);
            } else {
                throw new BussinessException("成交推送业务类型不正确");
            }
        } catch (Exception e) {
            e.printStackTrace();
            putToErrorCache(model);
            throw new BussinessException(e.getMessage());
        }

    }

    /**
     * 平仓成交回报处理
     *
     * @param model
     */
    private void doCoverDealPush(DealPush model) {
        // 委托编号
        String entrustNo = model.getFutuEntrustNo();
        // 查询未成交订单
        Order order = orderMapper.selectOne(new Order(t -> {
            t.setSettleEntrNo(entrustNo);
            t.setStatus(Order.STATUS.T4.code);
            t.setDelFlag(GenericPo.DELFLAG.NORMAL.code);
        }));

        if (order == null) {
            if (new Date().after(model.getDealOutTime())) {
                log.info("平仓成交处理超时，加入成交错误缓存，成交数据：" + JSON.toJSONString(model));
                putToErrorCache(model);
            } else {
                log.info("平仓成交未找到对应状态订单，重新加入队列，成交数据：" + JSON.toJSONString(model));
                redisService.push(CacheID.QUEUE_DEAL_PUSH, model);
            }
            return;
        }
        if (order.getStatus().intValue() != Order.STATUS.T4.code) throw new BussinessException("订单不存在或已平仓");

        // 每点合约价值
        BigDecimal pointValue = riskParamService.getParamValue(RiskNID.POINT_VALUE, order.getCommoId(), BigDecimal.class);
        // 最小波动点数
        BigDecimal waveMin = riskParamService.getParamValue(RiskNID.WAVE_POINT_MIN, order.getCommoId(), BigDecimal.class);
        // 汇率
        BigDecimal exchangeRate = riskParamService.getParamValue(RiskNID.EXCHANGE_RATE, order.getCommoId(), BigDecimal.class);

        //计算盈亏金额 ,全部转为人民币 (平仓价 - 开仓价)*汇率*数量*每点合约价值/最小波动点
        BigDecimal profit = model.getFutuBusinessPrice().subtract(order.getOpenPrice()).multiply(new BigDecimal(model.getBusinessAmount())).multiply(pointValue).multiply(exchangeRate).divide(waveMin, 2, BigDecimal.ROUND_HALF_UP);
        if (Order.DIRECTION.T2.code == order.getDirection().intValue()) {
            profit = profit.negate();
        }
        BigDecimal finalProfit = profit;

        // 计算盈亏点数  =  （平仓价 - 开仓价）/最小波动点数*手数  取整数
        int profitPoint = model.getFutuBusinessPrice().subtract(order.getOpenPrice()).multiply(new BigDecimal(model.getBusinessAmount())).divide(waveMin, 2, BigDecimal.ROUND_HALF_UP).intValue();
        if (Order.DIRECTION.T2.code == order.getDirection().intValue()) {
            profitPoint = profitPoint * (-1);
        }
        int finalProfitPoint = profitPoint;


        // 结算订单
        Order coverOrder;

        // 处理订单信息
        if (model.getBusinessAmount() == order.getNumber().intValue()) {
            // 全部平仓，更新订单状态
            int c = orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order.getId());
                t.setVersion(order.getVersion());
                t.setStatus(Order.STATUS.T6.code);
                t.setSettleTime(model.getDealTime());
                t.setSettlePrice(model.getFutuBusinessPrice());
                t.setSettleBusiNo(model.getFutuBusinessNo());
                t.setProfitPoint(finalProfitPoint);
                t.setProfit(finalProfit);
                t.setExRate(exchangeRate);
            }));
            if (c != 1) throw new BussinessException("期货成交回报更新订单状态失败");

            coverOrder = order;
        } else if (model.getBusinessAmount() < order.getNumber()) {
            // 部分平仓，进行拆单处理
            // 原订单号
            String orderNo = order.getOrderNo();

            String orderNo1;
            String orderNo2;
            // 计算新订单相关参数 履约保证金、止盈保证金（按数量比例分配）
            BigDecimal scale = new BigDecimal(model.getBusinessAmount()).divide(new BigDecimal(order.getNumber()), 8, BigDecimal.ROUND_HALF_UP);
            BigDecimal deposit = scale.multiply(order.getDeposit()).setScale(2, BigDecimal.ROUND_HALF_UP);
            BigDecimal profitDeposit = scale.multiply(order.getProfitDeposit()).setScale(2, BigDecimal.ROUND_HALF_UP);
            // 拆分订单积分
            Integer usePoint1 = 0;// 原订单积分数量
            Integer usePoint2 = 0;// 新订单积分数量
            if (order.getUsePoint() > 0) {
                usePoint2 = order.getUsePoint() * model.getBusinessAmount() / order.getNumber();
                usePoint1 = order.getUsePoint() - usePoint2;
            }

            // 处理订单号
            if (orderNo.contains("S")) {
                // 已拆分过
                orderNo1 = orderNo;
                orderNo2 = orderNo.substring(0, orderNo.indexOf("S")) + "S" + getLastPrefix(orderNo.substring(0, orderNo.indexOf("S")), "S");
            } else {
                // 未拆分过
                orderNo1 = orderNo + "S1";
                orderNo2 = orderNo + "S2";
            }

            // 更新原新单
            Integer finalUsePoint1 = usePoint1;
            if (orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order.getId());
                t.setVersion(order.getVersion());
                t.setOrderNo(orderNo1);
                t.setNumber(order.getNumber() - model.getBusinessAmount());
                t.setDeposit(order.getDeposit().subtract(deposit));
                t.setProfitDeposit(order.getProfitDeposit().subtract(profitDeposit));
                t.setUsePoint(finalUsePoint1);
            })) != 1) throw new BussinessException("更新原订单失败");

            // 新增成交订单
            Integer finalUsePoint2 = usePoint2;
            orderMapper.insert(new Order(t -> {
                BeanUtils.copyProperties(order, t);
                t.setId(null);
                t.setVersion(null);
                t.setOrderNo(orderNo2);
                t.setNumber(model.getBusinessAmount());
                t.setStatus(Order.STATUS.T6.code);
                t.setSettleTime(model.getDealTime());
                t.setSettlePrice(model.getFutuBusinessPrice());
                t.setSettleBusiNo(model.getFutuBusinessNo());
                t.setDeposit(deposit);
                t.setProfitDeposit(profitDeposit);
                t.setProfitPoint(finalProfitPoint);
                t.setProfit(finalProfit);
                t.setUsePoint(finalUsePoint2);
                t.setExRate(exchangeRate);
            }));

            coverOrder = orderMapper.selectOne(new Order(t -> {
                t.setOrderNo(orderNo2);
                t.setDelFlag(GenericPo.DELFLAG.NORMAL.code);
            }));
        } else {
            // 成交数量有误，保存错误数据
            throw new BussinessException("成交数量有误");
        }


        // 处理点买人投资人账户资金
        if (profit.compareTo(BigDecimal.ZERO) == 1) {
            // 计算实际盈利
            BigDecimal profitReal = profit.compareTo(coverOrder.getProfitDeposit()) == 1 ? coverOrder.getProfitDeposit().multiply(coverOrder.getAllotRate()) : profit.multiply(coverOrder.getAllotRate());

            // 处理点买人资金账户
            Account account = accountMapper.selectByUID(coverOrder.getUserId());
            int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), coverOrder.getDeposit(), AccountLog.TYPE.T19, account, "策略盈利，解冻履约保证金" + coverOrder.getDeposit() + "元。订单ID：" + coverOrder.getId()));
            if (c2 != 1) throw new BussinessException("策略盈利，解冻履约保证金失败");
            int c3 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), profitReal, AccountLog.TYPE.T25, account, "策略盈利，转入盈利收入" + profitReal + "元。订单ID：" + coverOrder.getId()));
            if (c3 != 1) throw new BussinessException("策略盈利，转入盈利收入失败");
            int c1 = accountMapper.updateAccount(profitReal, profitReal.add(coverOrder.getDeposit()), coverOrder.getDeposit().negate(), account.getUserId());
            if (c1 != 1) throw new BussinessException("期货平仓成交回报更新点买人资金账户失败");


            // 处理投资人资金账户
            Account ia = accountMapper.selectByUID(coverOrder.getInvestId());
            int ic2 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), coverOrder.getProfitDeposit(), AccountLog.TYPE.T22, ia, "点买人策略盈利，解冻止盈保证金" + coverOrder.getProfitDeposit() + "元。订单ID：" + coverOrder.getId()));
            if (ic2 != 1) throw new BussinessException("点买人策略盈利，解冻止盈保证金失败");
            int ic3 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), profitReal, AccountLog.TYPE.T26, ia, "点买人策略盈利，扣除盈利金额" + profitReal + "元。订单ID：" + coverOrder.getId()));
            if (ic3 != 1) throw new BussinessException("点买人策略盈利，扣除盈利金额失败");
            int ic1 = accountMapper.updateAccount(profitReal.negate(), profitReal.negate().add(coverOrder.getProfitDeposit()), coverOrder.getProfitDeposit().negate(), ia.getUserId());
            if (ic1 != 1) throw new BussinessException("期货平仓成交回报更新点买人资金账户失败");
        } else if (profit.compareTo(BigDecimal.ZERO) == -1) {
            // 计算实际亏损
            BigDecimal loseReal = profit.negate().compareTo(coverOrder.getDeposit()) == 1 ? coverOrder.getDeposit() : profit.negate();

            // 处理点买人资金账户
            Account account = accountMapper.selectByUID(coverOrder.getUserId());
            int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), coverOrder.getDeposit(), AccountLog.TYPE.T19, account, "策略亏损，解冻履约保证金" + coverOrder.getDeposit() + "元。订单ID：" + coverOrder.getId()));
            if (c2 != 1) throw new BussinessException("策略亏损，解冻履约保证金失败");
            int c3 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), loseReal, AccountLog.TYPE.T26, account, "策略亏损，扣除亏损金额" + loseReal + "元。订单ID：" + coverOrder.getId()));
            if (c3 != 1) throw new BussinessException("策略亏损，扣除亏损金额失败");
            int c1 = accountMapper.updateAccount(loseReal.negate(), coverOrder.getDeposit().subtract(loseReal), coverOrder.getDeposit().negate(), account.getUserId());
            if (c1 != 1) throw new BussinessException("期货平仓成交回报更新点买人资金账户失败");


            // 处理投资人资金账户
            Account ia = accountMapper.selectByUID(coverOrder.getInvestId());
            int ic2 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), coverOrder.getProfitDeposit(), AccountLog.TYPE.T22, ia, "点买人策略亏损，解冻止盈保证金" + coverOrder.getProfitDeposit() + "元。订单ID：" + coverOrder.getId()));
            if (ic2 != 1) throw new BussinessException("点买人策略亏损，解冻止盈保证金额失败");
            int ic3 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), loseReal, AccountLog.TYPE.T25, ia, "点买人策略亏损，转入亏损金额" + loseReal + "元。订单ID：" + coverOrder.getId()));
            if (ic3 != 1) throw new BussinessException("点买人策略亏损，转入亏损金额失败");
            int ic1 = accountMapper.updateAccount4Plat(loseReal, loseReal.add(coverOrder.getProfitDeposit()), coverOrder.getProfitDeposit().negate(), ia.getUserId());
            if (ic1 != 1) throw new BussinessException("期货平仓成交回报更新点买人资金账户失败");
        } else {
            // 实际无盈亏

            // 处理点买人资金账户
            Account account = accountMapper.selectByUID(coverOrder.getUserId());
            int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), coverOrder.getDeposit(), AccountLog.TYPE.T19, account, "策略无盈亏，解冻履约保证金" + coverOrder.getDeposit() + "元。订单ID：" + coverOrder.getId()));
            if (c2 != 1) throw new BussinessException("策略无盈亏，解冻履约保证金失败");
            int c1 = accountMapper.updateAccount(BigDecimal.ZERO, coverOrder.getDeposit(), coverOrder.getDeposit().negate(), account.getUserId());
            if (c1 != 1) throw new BussinessException("期货平仓成交回报更新点买人资金账户失败");


            // 处理投资人资金账户
            Account ia = accountMapper.selectByUID(coverOrder.getInvestId());
            int ic2 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), coverOrder.getProfitDeposit(), AccountLog.TYPE.T22, ia, "点买人策略无盈亏，解冻止盈保证金" + coverOrder.getProfitDeposit() + "元。订单ID：" + coverOrder.getId()));
            if (ic2 != 1) throw new BussinessException("点买人策略无盈亏，解冻止盈保证金额失败");
            int ic1 = accountMapper.updateAccount(BigDecimal.ZERO, coverOrder.getProfitDeposit(), coverOrder.getProfitDeposit().negate(), ia.getUserId());
            if (ic1 != 1) throw new BussinessException("期货平仓成交回报更新点买人资金账户失败");
        }
    }

    /**
     * 手动处理开仓成交回报
     */
    @Override
    public void ebtrustHandDealOrder(Order order, Integer openAmount, String openBusiNo, BigDecimal openPrice, Date openTime) {
        // 止盈倍数
        BigDecimal profitStopTimes = riskParamService.getParamValue(RiskNID.PROFIT_STOP_TIMES, order.getCommoId(), BigDecimal.class);
        // 止损下滑价格 = 止损点数 * 最小波动点数
        BigDecimal lsp = new BigDecimal(order.getLoseStopPoint()).multiply(riskParamService.getParamValue(RiskNID.WAVE_POINT_MIN, order.getCommoId(), BigDecimal.class)).setScale(4, BigDecimal.ROUND_HALF_UP);
        BigDecimal loseStopMinPrice;
        BigDecimal profitStopMaxPrice;
        if (order.getDirection().intValue() == Order.DIRECTION.T1.code) {
            // 最低止损价格 = 委托价 - 止损下滑价格
            loseStopMinPrice = openPrice.subtract(lsp).compareTo(BigDecimal.ZERO) != 1 ? BigDecimal.ZERO : openPrice.subtract(lsp);
            // 最高止盈价格 = 委托价 + （止损下滑价格 * 止盈倍数）
            profitStopMaxPrice = openPrice.add(lsp.multiply(profitStopTimes)).compareTo(BigDecimal.ZERO) != 1 ? BigDecimal.ZERO : openPrice.add(lsp.multiply(profitStopTimes));
        } else {
            // 最低止损价格 = 委托价 + 止损下滑价格
            loseStopMinPrice = openPrice.add(lsp).compareTo(BigDecimal.ZERO) != 1 ? BigDecimal.ZERO : openPrice.add(lsp);
            // 最高止盈价格 = 委托价 - （止损下滑价格 * 止盈倍数）
            profitStopMaxPrice = openPrice.subtract(lsp.multiply(profitStopTimes)).compareTo(BigDecimal.ZERO) != 1 ? BigDecimal.ZERO : openPrice.subtract(lsp.multiply(profitStopTimes));
        }

        if (openAmount == order.getNumber().intValue()) {
            // 全部成交，更新订单状态
            int c = orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order.getId());
                t.setVersion(order.getVersion());
                t.setStatus(Order.STATUS.T3.code);
                t.setOpenTime(openTime);
                t.setOpenPrice(openPrice);
                t.setOpenBusiNo(openBusiNo);

                if (order.getOpenWay().intValue() == Order.OPENWAY.T1.code) {
                    t.setProfitStopMaxPrice(profitStopMaxPrice);
                    t.setProfitStopPrice(profitStopMaxPrice);
                    t.setLoseStopMinPrice(loseStopMinPrice);
                    t.setLoseStopPrice(loseStopMinPrice);
                }
            }));
            if (c != 1) throw new BussinessException("期货成交回报更新订单状态失败");

            // 订单总手续费
            BigDecimal totalFee = order.getFee().multiply(new BigDecimal(order.getNumber())).setScale(2, BigDecimal.ROUND_HALF_UP);
            // 实际扣除的手续费 = 订单手续费 - 积分抵扣
            BigDecimal totalFeeReal = totalFee;
            if (order.getUsePoint() > 0 && totalFee.compareTo(new BigDecimal(order.getUsePoint())) > 0) {
                totalFeeReal = totalFee.subtract(new BigDecimal(order.getUsePoint()));
            } else if (order.getUsePoint() > 0 && totalFee.compareTo(new BigDecimal(order.getUsePoint())) <= 0) {
                totalFeeReal = BigDecimal.ZERO;
            }


            // 扣除点买人冻结的手续费
            Account account = accountMapper.selectByUID(order.getUserId());
            if (order.getUsePoint() > 0) {
                if (totalFeeReal.compareTo(BigDecimal.ZERO) > 0) {
                    int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), totalFeeReal, AccountLog.TYPE.T17, account, "委托成交，扣除交易综合费" + totalFee + "元，积分抵扣：" + order.getUsePoint() + "。订单ID：" + order.getId()));
                    if (c2 != 1) throw new BussinessException("期货成交回报扣除交易综合费失败资金记录保存失败");
                }
            } else {
                int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), totalFeeReal, AccountLog.TYPE.T17, account, "委托成交，扣除交易综合费" + totalFee + "元。订单ID：" + order.getId()));
                if (c2 != 1) throw new BussinessException("期货成交回报扣除交易综合费失败资金记录保存失败");
            }
            if (totalFeeReal.compareTo(BigDecimal.ZERO) == 1) {
                int c1 = accountMapper.updateAccount(totalFeeReal.negate(), BigDecimal.ZERO, totalFeeReal.negate(), account.getUserId());
                if (c1 != 1) throw new BussinessException("期货成交回报扣除交易综合费失败");
            }


        } else if (openAmount < order.getNumber()) {
            // 部分成交，进行拆单处理
            // 原订单号
            String orderNo = order.getOrderNo();

            String orderNo1;
            String orderNo2;
            // 计算新订单相关参数 履约保证金、止盈保证金（按数量比例分配）
            BigDecimal scale = new BigDecimal(openAmount).divide(new BigDecimal(order.getNumber()), 8, BigDecimal.ROUND_HALF_UP);
            BigDecimal deposit = scale.multiply(order.getDeposit()).setScale(2, BigDecimal.ROUND_HALF_UP);
            BigDecimal profitDeposit = scale.multiply(order.getProfitDeposit()).setScale(2, BigDecimal.ROUND_HALF_UP);
            // 拆分订单积分
            Integer usePoint1 = 0;// 原订单积分数量
            Integer usePoint2 = 0;// 新订单积分数量
            if (order.getUsePoint() > 0) {
                usePoint2 = order.getUsePoint() * openAmount / order.getNumber();
                usePoint1 = order.getUsePoint() - usePoint2;
            }

            // 处理订单号
            if (orderNo.contains("D")) {
                // 已拆分过
                orderNo1 = orderNo;
                orderNo2 = orderNo.substring(0, orderNo.indexOf("D")) + "D" + getLastPrefix(orderNo.substring(0, orderNo.indexOf("D")), "D");
            } else {
                // 未拆分过
                orderNo1 = orderNo + "D1";
                orderNo2 = orderNo + "D2";
            }

            // 更新原新单
            Integer finalUsePoint1 = usePoint1;
            if (orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order.getId());
                t.setVersion(order.getVersion());
                t.setOrderNo(orderNo1);
                t.setNumber(order.getNumber() - openAmount);
                t.setDeposit(order.getDeposit().subtract(deposit));
                t.setProfitDeposit(order.getProfitDeposit().subtract(profitDeposit));
                t.setUsePoint(finalUsePoint1);
            })) != 1) throw new BussinessException("更新原订单失败");

            // 新增成交订单
            Integer finalUsePoint2 = usePoint2;
            orderMapper.insert(new Order(t -> {
                BeanUtils.copyProperties(order, t);
                t.setId(null);
                t.setVersion(null);
                t.setOrderNo(orderNo2);
                t.setNumber(openAmount);
                t.setStatus(Order.STATUS.T3.code);
                t.setOpenTime(openTime);
                t.setOpenPrice(openPrice);
                t.setOpenBusiNo(openBusiNo);
                t.setDeposit(deposit);
                t.setProfitDeposit(profitDeposit);
                t.setUsePoint(finalUsePoint2);

                if (order.getOpenWay().intValue() == Order.OPENWAY.T1.code) {
                    t.setLoseStopPrice(loseStopMinPrice);
                    t.setLoseStopMinPrice(loseStopMinPrice);
                    t.setProfitStopPrice(profitStopMaxPrice);
                    t.setProfitStopMaxPrice(profitStopMaxPrice);
                }
            }));
            Order order2 = orderMapper.selectOne(new Order(t -> {
                t.setOrderNo(orderNo2);
                t.setDelFlag(GenericPo.DELFLAG.NORMAL.code);
            }));

            // 订单总手续费
            BigDecimal totalFee = order2.getFee().multiply(new BigDecimal(order2.getNumber())).setScale(2, BigDecimal.ROUND_HALF_UP);
            // 实际扣除的手续费 = 订单手续费 - 积分抵扣
            BigDecimal totalFeeReal = totalFee;
            if (order2.getUsePoint() > 0 && totalFee.compareTo(new BigDecimal(order2.getUsePoint())) > 0) {
                totalFeeReal = totalFee.subtract(new BigDecimal(order2.getUsePoint()));
            } else if (order2.getUsePoint() > 0 && totalFee.compareTo(new BigDecimal(order2.getUsePoint())) <= 0) {
                totalFeeReal = BigDecimal.ZERO;
            }

            // 扣除点买人冻结的手续费
            Account account = accountMapper.selectByUID(order.getUserId());
            if (order2.getUsePoint() > 0) {
                if (totalFeeReal.compareTo(BigDecimal.ZERO) > 0) {
                    int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), totalFeeReal, AccountLog.TYPE.T17, account, "扣除交易综合费" + totalFee + "元，积分抵扣：" + order2.getUsePoint() + "。订单ID：" + order2.getId()));
                    if (c2 != 1) throw new BussinessException("期货成交回报扣除交易综合费失败资金记录保存失败");
                }
            } else {
                int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), totalFeeReal, AccountLog.TYPE.T17, account, "扣除交易综合费" + totalFee + "元。订单ID：" + order2.getId()));
                if (c2 != 1) throw new BussinessException("期货成交回报扣除交易综合费失败资金记录保存失败");
            }
            if (totalFeeReal.compareTo(BigDecimal.ZERO) == 1) {
                int c1 = accountMapper.updateAccount(totalFeeReal.negate(), BigDecimal.ZERO, totalFeeReal.negate(), account.getUserId());
                if (c1 != 1) throw new BussinessException("期货成交回报扣除交易综合费失败");
            }
        } else {
            // 成交数量有误，保存错误数据
            throw new BussinessException("成交数量有误");
        }


        try {
            // 发放积分
            pointRuleService.sendPoint(order.getUserId(), PointNid.T6);

            // 发放推荐佣金
            User user = userMapper.selectByPrimaryKey(order.getUserId());
            if (user == null && user.getParentId() == null && user.getParentId() == 0) return;
            User parent = userMapper.selectByPrimaryKey(user.getParentId());
            if (parent == null) return;
            // 计算返佣金额 = 手续费 * 返佣比例
            BigDecimal reward = order.getFee().multiply(new BigDecimal(openAmount)).multiply(parent.getRewardScale()).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP);
            if (reward.compareTo(BigDecimal.ZERO) == 1) {
                Account pa = accountMapper.selectByUID(parent.getId());
                if (accountLogMapper.insert(AccountLog.buildLog(parent.getId(), reward, AccountLog.TYPE.T11, pa, "获得推广佣金" + reward + "元。")) != 1)
                    throw new BussinessException("保存推广佣金资金记录失败");
                if (accountMapper.updateAccount(reward, reward, BigDecimal.ZERO, parent.getId()) != 1)
                    throw new BussinessException("发放推广佣金失败");
            }
        } catch (Exception e) {
            log.error("处理积分发放或推广佣金失败，错误消息：{}", e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 手动处理平仓成交回报
     */
    @Override
    public void balanceHandDealOrder(Order order, Integer settleAmount, String settleBusiNo, BigDecimal settlePrice, Date settleTime) throws Exception {
        // 每点合约价值
        BigDecimal pointValue = riskParamService.getParamValue(RiskNID.POINT_VALUE, order.getCommoId(), BigDecimal.class);
        // 最小波动点数
        BigDecimal waveMin = riskParamService.getParamValue(RiskNID.WAVE_POINT_MIN, order.getCommoId(), BigDecimal.class);
        // 汇率
        BigDecimal exchangeRate = riskParamService.getParamValue(RiskNID.EXCHANGE_RATE, order.getCommoId(), BigDecimal.class);

        //计算盈亏金额 ,全部转为人民币 (平仓价 - 开仓价)*汇率*数量*每点合约价值/最小波动点
        BigDecimal profit = settlePrice.subtract(order.getOpenPrice()).multiply(new BigDecimal(settleAmount)).multiply(pointValue).multiply(exchangeRate).divide(waveMin, 2, BigDecimal.ROUND_HALF_UP);
        // 计算盈亏点数  =  （平仓价 - 开仓价）/最小波动点数*手数  取整数
        int profitPoint = settlePrice.subtract(order.getOpenPrice()).multiply(new BigDecimal(settleAmount)).divide(waveMin, 2, BigDecimal.ROUND_HALF_UP).intValue();
        if (Order.DIRECTION.T2.code == order.getDirection().intValue()) {
            profit = profit.negate();
            profitPoint = profitPoint * (-1);
        }
        BigDecimal finalProfit = profit;
        int finalProfitPoint = profitPoint;

        //初始化一个finalOrder用于处理账户资金
        Order finalOrder = null;
        // 处理订单信息
        if (settleAmount == order.getNumber().intValue()) {
            // 全部平仓，更新订单状态
            int c = orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order.getId());
                t.setVersion(order.getVersion());
                t.setStatus(Order.STATUS.T6.code);
                t.setSettleTime(settleTime);
                t.setSettlePrice(settlePrice);
                t.setSettleBusiNo(settleBusiNo);
                t.setProfitPoint(finalProfitPoint);
                t.setProfit(finalProfit);
                t.setExRate(exchangeRate);
            }));
            if (c != 1) throw new BussinessException("期货成交回报更新订单状态失败");
            finalOrder = order;
        } else if (settleAmount < order.getNumber()) {
            // 部分平仓，进行拆单处理
            // 原订单号
            String orderNo = order.getOrderNo();

            String orderNo1;
            String orderNo2;
            // 计算新订单相关参数 履约保证金、止盈保证金（按数量比例分配）
            BigDecimal scale = new BigDecimal(settleAmount).divide(new BigDecimal(order.getNumber()), 8, BigDecimal.ROUND_HALF_UP);
            BigDecimal deposit = scale.multiply(order.getDeposit()).setScale(2, BigDecimal.ROUND_HALF_UP);
            BigDecimal profitDeposit = scale.multiply(order.getProfitDeposit()).setScale(2, BigDecimal.ROUND_HALF_UP);
            // 拆分订单积分
            Integer usePoint1 = 0;// 原订单积分数量
            Integer usePoint2 = 0;// 新订单积分数量
            if (order.getUsePoint() > 0) {
                usePoint2 = order.getUsePoint() * settleAmount / order.getNumber();
                usePoint1 = order.getUsePoint() - usePoint2;
            }

            // 处理订单号
            if (orderNo.contains("S")) {
                // 已拆分过
                orderNo1 = orderNo;
                orderNo2 = orderNo.substring(0, orderNo.indexOf("S")) + "S" + getLastPrefix(orderNo.substring(0, orderNo.indexOf("S")), "S");
            } else {
                // 未拆分过
                orderNo1 = orderNo + "S1";
                orderNo2 = orderNo + "S2";
            }

            // 更新原新单
            Integer finalUsePoint1 = usePoint1;
            if (orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order.getId());
                t.setVersion(order.getVersion());
                t.setOrderNo(orderNo1);
                t.setNumber(order.getNumber() - settleAmount);
                t.setDeposit(order.getDeposit().subtract(deposit));
                t.setProfitDeposit(order.getProfitDeposit().subtract(profitDeposit));
                t.setUsePoint(finalUsePoint1);
            })) != 1) throw new BussinessException("更新原订单失败");

            // 新增成交订单
            Integer finalUsePoint2 = usePoint2;
            orderMapper.insert(new Order(t -> {
                BeanUtils.copyProperties(order, t);
                t.setId(null);
                t.setVersion(null);
                t.setOrderNo(orderNo2);
                t.setNumber(settleAmount);
                t.setStatus(Order.STATUS.T6.code);
                t.setSettleTime(new Date());
                t.setSettlePrice(settlePrice);
                t.setSettleBusiNo(settleBusiNo);
                t.setDeposit(deposit);
                t.setProfitDeposit(profitDeposit);
                t.setProfitPoint(finalProfitPoint);
                t.setProfit(finalProfit);
                t.setUsePoint(finalUsePoint2);
                t.setExRate(exchangeRate);
            }));
            finalOrder = orderMapper.selectOne(new Order(o -> {
                o.setOrderNo(orderNo2);
                o.setDelFlag(Order.DELFLAG.NORMAL.code);
            }));
        } else {
            // 成交数量有误，保存错误数据
            throw new BussinessException("成交数量有误");
        }


        // 处理点买人投资人账户资金
        if (profit.compareTo(BigDecimal.ZERO) == 1) {
            // 计算实际盈利
            BigDecimal profitReal = profit.compareTo(finalOrder.getProfitDeposit()) == 1 ? finalOrder.getProfitDeposit().multiply(finalOrder.getAllotRate()) : profit.multiply(finalOrder.getAllotRate());

            // 处理点买人资金账户
            Account account = accountMapper.selectByUID(finalOrder.getUserId());
            int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), finalOrder.getDeposit(), AccountLog.TYPE.T19, account, "策略盈利，解冻履约保证金" + finalOrder.getDeposit() + "元。订单ID：" + finalOrder.getId()));
            if (c2 != 1) throw new BussinessException("策略盈利，解冻履约保证金失败");
            int c3 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), profitReal, AccountLog.TYPE.T25, account, "策略盈利，转入盈利收入" + profitReal + "元。订单ID：" + finalOrder.getId()));
            if (c3 != 1) throw new BussinessException("策略盈利，转入盈利收入失败");
            int c1 = accountMapper.updateAccount(profitReal, profitReal.add(finalOrder.getDeposit()), finalOrder.getDeposit().negate(), account.getUserId());
            if (c1 != 1) throw new BussinessException("期货手动平仓更新点买人资金账户失败");

            // 处理投资人资金账户
            Account ia = accountMapper.selectByUID(finalOrder.getInvestId());
            int ic2 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), finalOrder.getProfitDeposit(), AccountLog.TYPE.T22, ia, "点买人策略盈利，解冻止盈保证金" + finalOrder.getProfitDeposit() + "元。订单ID：" + finalOrder.getId()));
            if (ic2 != 1) throw new BussinessException("点买人策略盈利，解冻止盈保证金失败");
            int ic3 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), profitReal, AccountLog.TYPE.T26, ia, "点买人策略盈利，扣除盈利金额" + profitReal + "元。订单ID：" + finalOrder.getId()));
            if (ic3 != 1) throw new BussinessException("点买人策略盈利，扣除盈利金额失败");
            int ic1 = accountMapper.updateAccount(profitReal.negate(), profitReal.negate().add(finalOrder.getProfitDeposit()), finalOrder.getProfitDeposit().negate(), ia.getUserId());
            if (ic1 != 1) throw new BussinessException("期货手动平仓更新点买人资金账户失败");
        } else if (profit.compareTo(BigDecimal.ZERO) == -1) {
            // 计算实际亏损
            BigDecimal loseReal = profit.negate().compareTo(finalOrder.getDeposit()) == 1 ? finalOrder.getDeposit() : profit.negate();
            // 处理点买人资金账户
            Account account = accountMapper.selectByUID(finalOrder.getUserId());
            int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), finalOrder.getDeposit(), AccountLog.TYPE.T19, account, "策略亏损，解冻履约保证金" + finalOrder.getDeposit() + "元。订单ID：" + finalOrder.getId()));
            if (c2 != 1) throw new BussinessException("策略亏损，解冻履约保证金失败");
            int c3 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), loseReal, AccountLog.TYPE.T26, account, "策略亏损，扣除亏损金额" + loseReal + "元。订单ID：" + finalOrder.getId()));
            if (c3 != 1) throw new BussinessException("策略亏损，扣除亏损金额失败");
            int c1 = accountMapper.updateAccount(loseReal.negate(), finalOrder.getDeposit().subtract(loseReal), finalOrder.getDeposit().negate(), account.getUserId());
            if (c1 != 1) throw new BussinessException("期货手动平仓点买人资金账户失败");


            // 处理投资人资金账户
            Account ia = accountMapper.selectByUID(finalOrder.getInvestId());
            int ic2 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), finalOrder.getProfitDeposit(), AccountLog.TYPE.T22, ia, "点买人策略亏损，解冻止盈保证金" + finalOrder.getProfitDeposit() + "元。订单ID：" + finalOrder.getId()));
            if (ic2 != 1) throw new BussinessException("点买人策略亏损，解冻止盈保证金额失败");
            int ic3 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), loseReal, AccountLog.TYPE.T25, ia, "点买人策略亏损，转入亏损金额" + loseReal + "元。订单ID：" + finalOrder.getId()));
            if (ic3 != 1) throw new BussinessException("点买人策略亏损，转入亏损金额失败");
            int ic1 = accountMapper.updateAccount(loseReal, loseReal.add(finalOrder.getProfitDeposit()), finalOrder.getProfitDeposit().negate(), ia.getUserId());
            if (ic1 != 1) throw new BussinessException("期货手动平仓点买人资金账户失败");
        } else {
            // 实际无盈亏

            // 处理点买人资金账户
            Account account = accountMapper.selectByUID(finalOrder.getUserId());
            int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), finalOrder.getDeposit(), AccountLog.TYPE.T19, account, "策略无盈亏，解冻履约保证金" + finalOrder.getDeposit() + "元。订单ID：" + finalOrder.getId()));
            if (c2 != 1) throw new BussinessException("策略无盈亏，解冻履约保证金失败");
            int c1 = accountMapper.updateAccount(BigDecimal.ZERO, finalOrder.getDeposit(), finalOrder.getDeposit().negate(), account.getUserId());
            if (c1 != 1) throw new BussinessException("期货手动平仓成交回报更新点买人资金账户失败");


            // 处理投资人资金账户
            Account ia = accountMapper.selectByUID(finalOrder.getInvestId());
            int ic2 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), finalOrder.getProfitDeposit(), AccountLog.TYPE.T22, ia, "点买人策略无盈亏，解冻止盈保证金" + finalOrder.getProfitDeposit() + "元。订单ID：" + finalOrder.getId()));
            if (ic2 != 1) throw new BussinessException("点买人策略无盈亏，解冻止盈保证金额失败");
            int ic1 = accountMapper.updateAccount(BigDecimal.ZERO, finalOrder.getProfitDeposit(), finalOrder.getProfitDeposit().negate(), ia.getUserId());
            if (ic1 != 1) throw new BussinessException("期货手动平仓成交回报更新点买人资金账户失败");
        }
    }

    /**
     * 开仓成交回报处理
     *
     * @param model
     */
    private void doOpenDealPush(DealPush model) {
        // 委托编号
        String entrustNo = model.getFutuEntrustNo();
        // 查询未成交订单
        Order order = orderMapper.selectOne(new Order(t -> {
            t.setOpenEntrNo(entrustNo);
            t.setStatus(Order.STATUS.T2.code);
            t.setDelFlag(GenericPo.DELFLAG.NORMAL.code);
        }));
        if (order == null) {
            if (new Date().after(model.getDealOutTime())) {
                log.info("开仓成交处理超时，加入成交错误缓存，成交数据：" + JSON.toJSONString(model));
                putToErrorCache(model);
            } else {
                log.info("开仓成交未找到对应状态订单，重新加入队列，成交数据：" + JSON.toJSONString(model));
                redisService.push(CacheID.QUEUE_DEAL_PUSH, model);
            }
            return;
        }

        if (order.getStatus().intValue() != Order.STATUS.T2.code) throw new BussinessException("订单不存在或已成交");


        // 止盈倍数
        BigDecimal profitStopTimes = riskParamService.getParamValue(RiskNID.PROFIT_STOP_TIMES, order.getCommoId(), BigDecimal.class);
        // 止损下滑价格 = 止损点数 * 最小波动点数

        BigDecimal lsp = new BigDecimal(order.getLoseStopPoint()).multiply(riskParamService.getParamValue(RiskNID.WAVE_POINT_MIN, order.getCommoId(), BigDecimal.class)).setScale(4, BigDecimal.ROUND_HALF_UP);
        BigDecimal loseStopMinPrice;
        BigDecimal profitStopMaxPrice;
        if (order.getDirection().intValue() == Order.DIRECTION.T1.code) {
            // 最低止损价格 = 委托价 - 止损下滑价格
            loseStopMinPrice = model.getFutuBusinessPrice().subtract(lsp).compareTo(BigDecimal.ZERO) != 1 ? BigDecimal.ZERO : model.getFutuBusinessPrice().subtract(lsp);
            // 最高止盈价格 = 委托价 + （止损下滑价格 * 止盈倍数）
            profitStopMaxPrice = model.getFutuBusinessPrice().add(lsp.multiply(profitStopTimes)).compareTo(BigDecimal.ZERO) != 1 ? BigDecimal.ZERO : model.getFutuBusinessPrice().add(lsp.multiply(profitStopTimes));
        } else {
            // 最低止损价格 = 委托价 + 止损下滑价格
            loseStopMinPrice = model.getFutuBusinessPrice().add(lsp).compareTo(BigDecimal.ZERO) != 1 ? BigDecimal.ZERO : model.getFutuBusinessPrice().add(lsp);
            // 最高止盈价格 = 委托价 - （止损下滑价格 * 止盈倍数）
            profitStopMaxPrice = model.getFutuBusinessPrice().subtract(lsp.multiply(profitStopTimes)).compareTo(BigDecimal.ZERO) != 1 ? BigDecimal.ZERO : model.getFutuBusinessPrice().subtract(lsp.multiply(profitStopTimes));
        }

        if (model.getBusinessAmount() == order.getNumber().intValue()) {
            // 全部成交，更新订单状态
            int c = orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order.getId());
                t.setVersion(order.getVersion());
                t.setStatus(Order.STATUS.T3.code);
                t.setOpenTime(model.getDealTime());
                t.setOpenPrice(model.getFutuBusinessPrice());
                t.setOpenBusiNo(model.getFutuBusinessNo());

                if (order.getOpenWay().intValue() == Order.OPENWAY.T1.code) {
                    t.setProfitStopMaxPrice(profitStopMaxPrice);
                    t.setProfitStopPrice(profitStopMaxPrice);
                    t.setLoseStopMinPrice(loseStopMinPrice);
                    t.setLoseStopPrice(loseStopMinPrice);
                }
            }));
            if (c != 1) throw new BussinessException("期货成交回报更新订单状态失败");

            // 订单总手续费
            BigDecimal totalFee = order.getFee().multiply(new BigDecimal(order.getNumber())).setScale(2, BigDecimal.ROUND_HALF_UP);
            // 实际扣除的手续费 = 订单手续费 - 积分抵扣
            BigDecimal totalFeeReal = totalFee;
            if (order.getUsePoint() > 0 && totalFee.compareTo(new BigDecimal(order.getUsePoint())) > 0) {
                totalFeeReal = totalFee.subtract(new BigDecimal(order.getUsePoint()));
            } else if (order.getUsePoint() > 0 && totalFee.compareTo(new BigDecimal(order.getUsePoint())) <= 0) {
                totalFeeReal = BigDecimal.ZERO;
            }


            // 扣除点买人冻结的手续费
            Account account = accountMapper.selectByUID(order.getUserId());
            if (order.getUsePoint() > 0) {
                if (totalFeeReal.compareTo(BigDecimal.ZERO) > 0) {
                    int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), totalFeeReal, AccountLog.TYPE.T17, account, "委托成交，扣除交易综合费" + totalFee + "元，积分抵扣：" + order.getUsePoint() + "。订单ID：" + order.getId()));
                    if (c2 != 1) throw new BussinessException("期货成交回报扣除交易综合费失败资金记录保存失败");
                }
            } else {
                int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), totalFeeReal, AccountLog.TYPE.T17, account, "委托成交，扣除交易综合费" + totalFee + "元。订单ID：" + order.getId()));
                if (c2 != 1) throw new BussinessException("期货成交回报扣除交易综合费失败资金记录保存失败");
            }
            if (totalFeeReal.compareTo(BigDecimal.ZERO) == 1) {
                int c1 = accountMapper.updateAccount(totalFeeReal.negate(), BigDecimal.ZERO, totalFeeReal.negate(), account.getUserId());
                if (c1 != 1) throw new BussinessException("期货成交回报扣除交易综合费失败");
            }


        } else if (model.getBusinessAmount() < order.getNumber()) {
            // 部分成交，进行拆单处理
            // 原订单号
            String orderNo = order.getOrderNo();

            String orderNo1;
            String orderNo2;
            // 计算新订单相关参数 履约保证金、止盈保证金（按数量比例分配）
            BigDecimal scale = new BigDecimal(model.getBusinessAmount()).divide(new BigDecimal(order.getNumber()), 8, BigDecimal.ROUND_HALF_UP);
            BigDecimal deposit = scale.multiply(order.getDeposit()).setScale(2, BigDecimal.ROUND_HALF_UP);
            BigDecimal profitDeposit = scale.multiply(order.getProfitDeposit()).setScale(2, BigDecimal.ROUND_HALF_UP);
            // 拆分订单积分
            Integer usePoint1 = 0;// 原订单积分数量
            Integer usePoint2 = 0;// 新订单积分数量
            if (order.getUsePoint() > 0) {
                usePoint2 = order.getUsePoint() * model.getBusinessAmount() / order.getNumber();
                usePoint1 = order.getUsePoint() - usePoint2;
            }

            // 处理订单号
            if (orderNo.contains("D")) {
                // 已拆分过
                orderNo1 = orderNo;
                orderNo2 = orderNo.substring(0, orderNo.indexOf("D")) + "D" + getLastPrefix(orderNo.substring(0, orderNo.indexOf("D")), "D");
            } else {
                // 未拆分过
                orderNo1 = orderNo + "D1";
                orderNo2 = orderNo + "D2";
            }

            // 更新原新单
            Integer finalUsePoint1 = usePoint1;
            if (orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order.getId());
                t.setVersion(order.getVersion());
                t.setOrderNo(orderNo1);
                t.setNumber(order.getNumber() - model.getBusinessAmount());
                t.setDeposit(order.getDeposit().subtract(deposit));
                t.setProfitDeposit(order.getProfitDeposit().subtract(profitDeposit));
                t.setUsePoint(finalUsePoint1);
            })) != 1) throw new BussinessException("更新原订单失败");

            // 新增成交订单
            Integer finalUsePoint2 = usePoint2;
            orderMapper.insert(new Order(t -> {
                BeanUtils.copyProperties(order, t);
                t.setId(null);
                t.setVersion(null);
                t.setOrderNo(orderNo2);
                t.setNumber(model.getBusinessAmount());
                t.setStatus(Order.STATUS.T3.code);
                t.setOpenTime(model.getDealTime());
                t.setOpenPrice(model.getFutuBusinessPrice());
                t.setOpenBusiNo(model.getFutuBusinessNo());
                t.setDeposit(deposit);
                t.setProfitDeposit(profitDeposit);
                t.setUsePoint(finalUsePoint2);

                if (order.getOpenWay().intValue() == Order.OPENWAY.T1.code) {
                    t.setLoseStopPrice(loseStopMinPrice);
                    t.setLoseStopMinPrice(loseStopMinPrice);
                    t.setProfitStopPrice(profitStopMaxPrice);
                    t.setProfitStopMaxPrice(profitStopMaxPrice);
                }
            }));
            Order order2 = orderMapper.selectOne(new Order(o -> {
                o.setOrderNo(orderNo2);
                o.setDelFlag(Order.DELFLAG.NORMAL.code);
            }));

            // 订单总手续费
            BigDecimal totalFee = order2.getFee().multiply(new BigDecimal(order2.getNumber())).setScale(2, BigDecimal.ROUND_HALF_UP);
            // 实际扣除的手续费 = 订单手续费 - 积分抵扣
            BigDecimal totalFeeReal = totalFee;
            if (order2.getUsePoint() > 0 && totalFee.compareTo(new BigDecimal(order2.getUsePoint())) > 0) {
                totalFeeReal = totalFee.subtract(new BigDecimal(order2.getUsePoint()));
            } else if (order2.getUsePoint() > 0 && totalFee.compareTo(new BigDecimal(order2.getUsePoint())) <= 0) {
                totalFeeReal = BigDecimal.ZERO;
            }

            // 扣除点买人冻结的手续费
            Account account = accountMapper.selectByUID(order.getUserId());
            if (order2.getUsePoint() > 0) {
                if (totalFeeReal.compareTo(BigDecimal.ZERO) > 0) {
                    int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), totalFeeReal, AccountLog.TYPE.T17, account, "扣除交易综合费" + totalFee + "元，积分抵扣：" + order2.getUsePoint() + "。订单ID：" + order2.getId()));
                    if (c2 != 1) throw new BussinessException("期货成交回报扣除交易综合费失败资金记录保存失败");
                }
            } else {
                int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), totalFeeReal, AccountLog.TYPE.T17, account, "扣除交易综合费" + totalFee + "元。订单ID：" + order2.getId()));
                if (c2 != 1) throw new BussinessException("期货成交回报扣除交易综合费失败资金记录保存失败");
            }
            if (totalFeeReal.compareTo(BigDecimal.ZERO) == 1) {
                int c1 = accountMapper.updateAccount(totalFeeReal.negate(), BigDecimal.ZERO, totalFeeReal.negate(), account.getUserId());
                if (c1 != 1) throw new BussinessException("期货成交回报扣除交易综合费失败");
            }
        } else {
            // 成交数量有误，保存错误数据
            throw new BussinessException("成交数量有误");
        }


        try {
            // 发放积分
            pointRuleService.sendPoint(order.getUserId(), PointNid.T6);

            // 发放推荐佣金
            User user = userMapper.selectByPrimaryKey(order.getUserId());
            if (user == null && user.getParentId() == null && user.getParentId() == 0) return;
            User parent = userMapper.selectByPrimaryKey(user.getParentId());
            if (parent == null) return;
            // 计算返佣金额 = 手续费 * 返佣比例
            BigDecimal reward = order.getFee().multiply(new BigDecimal(model.getBusinessAmount())).multiply(parent.getRewardScale()).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP);
            if (reward.compareTo(BigDecimal.ZERO) == 1) {
                Account pa = accountMapper.selectByUID(parent.getId());
                if (accountLogMapper.insert(AccountLog.buildLog(parent.getId(), reward, AccountLog.TYPE.T11, pa, "获得推广佣金" + reward + "元。")) != 1)
                    throw new BussinessException("保存推广佣金资金记录失败");
                if (accountMapper.updateAccount(reward, reward, BigDecimal.ZERO, parent.getId()) != 1)
                    throw new BussinessException("发放推广佣金失败");
            }
        } catch (Exception e) {
            log.error("处理积分发放或推广佣金失败，错误消息：{}", e.getMessage());
            e.printStackTrace();
        }
    }


    /**
     * 成交回报处理错误缓存
     *
     * @param model
     */
    private void putToErrorCache(DealPush model) {
        List<DealPush> list = redisService.getList(CacheID.BACK_DEAL_ERROR_LIST, DealPush.class);
        if (list == null) {
            list = new ArrayList<>();
        }
        list.add(model);
        redisService.put(CacheID.BACK_DEAL_ERROR_LIST, list);
    }

    /**
     * 委托回报
     *
     * @param model 回报参数
     */
    @Override
    public void doEntrustPush(EntrustPush model) {
        // 委托编号
        String entrustNo = model.getFutuEntrustNo();

        if (StrUtil.isBlank(entrustNo)) {
            log.info("委托回报没有委托编号，不予处理！");
            return;
        }
        switch (model.getEntrustReference()) {
            case EntrustType.OPEN: // 开仓委托回报
                // 查询未成交订单
                Order eorder = orderMapper.selectOne(new Order(t -> {
                    t.setOpenEntrNo(entrustNo);
                    t.setDelFlag(GenericPo.DELFLAG.NORMAL.code);
                    t.setStatus(Order.STATUS.T2.code);
                }));
                if ("6".equals(model.getFutuEntrustStatus()) || "9".equals(model.getFutuEntrustStatus())) {
                    // 已撤、废单
                    doOrderCancle(eorder, Order.STATUS.T10, Order.CANCLETYPE.T5);
                } else {
                    // 其他状态不处理
                }
                break;
            case EntrustType.SETTLE: // 平仓委托回报
                // 查询未成交订单
                Order sorder = orderMapper.selectOne(new Order(t -> {
                    t.setSettleEntrNo(entrustNo);
                    t.setDelFlag(GenericPo.DELFLAG.NORMAL.code);
                    t.setStatus(Order.STATUS.T4.code);
                }));
                if ("6".equals(model.getFutuEntrustStatus()) || "9".equals(model.getFutuEntrustStatus())) {
                    // 已撤、废单，更新订单状态为持仓中
                    orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                        t.setId(sorder.getId());
                        t.setVersion(sorder.getVersion());
                        t.setStatus(Order.STATUS.T3.code);
                    }));
                } else {
                    // 其他状态不处理
                }
                break;
            case EntrustType.CANCLE: // 撤单委托回报
                // 查询未成交订单
                Order corder = orderMapper.selectOne(new Order(t -> {
                    t.setCancleEntrNo(entrustNo);
                    t.setDelFlag(GenericPo.DELFLAG.NORMAL.code);
                    t.setStatus(Order.STATUS.T9.code);
                }));

                Account account = accountMapper.selectByUID(corder.getUserId());
                Account ia = accountMapper.selectByUID(corder.getInvestId());
                if ("6".equals(model.getFutuEntrustStatus())) {
                    // 已撤、废单
                    doOrderCancle(corder, Order.STATUS.T10, Order.CANCLETYPE.T5);
                } else if ("5".equals(model.getFutuEntrustStatus())) {
                    // 部撤，处理部分撤单
                    // 部分撤单数量
                    int num = model.getEntrustAmount() - model.getBusinessAmount();
                    if (corder.getNumber().intValue() == num) {
                        // 更新原新单
                        if (orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                            t.setId(corder.getId());
                            t.setVersion(corder.getVersion());
                            t.setStatus(Order.STATUS.T10.code);
                            t.setCancleTime(new Date());
                            t.setCancleType(Order.CANCLETYPE.T5.code);
                        })) != 1) throw new BussinessException("更新原订单失败");

                        // 订单总手续费
                        BigDecimal totalFee = corder.getFee().multiply(new BigDecimal(num)).setScale(2, BigDecimal.ROUND_HALF_UP);
                        // 实际扣除的手续费 = 订单手续费 - 积分抵扣
                        BigDecimal totalFeeReal = totalFee;
                        if (corder.getUsePoint() > 0 && totalFee.compareTo(new BigDecimal(corder.getUsePoint())) > 0) {
                            totalFeeReal = totalFee.subtract(new BigDecimal(corder.getUsePoint()));
                        } else if (corder.getUsePoint() > 0 && totalFee.compareTo(new BigDecimal(corder.getUsePoint())) <= 0) {
                            totalFeeReal = BigDecimal.ZERO;
                        }
                        // 处理账户资金
                        int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), corder.getDeposit(), AccountLog.TYPE.T19, account, "撤单成功，解冻履约保证金" + corder.getDeposit() + "元。订单ID：" + corder.getId()));
                        if (c2 != 1) throw new BussinessException("撤单成功，解冻履约保证金失败");
                        if (corder.getUsePoint() > 0) {
                            if (totalFeeReal.compareTo(BigDecimal.ZERO) > 0) {
                                int c3 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), totalFeeReal, AccountLog.TYPE.T16, account, "撤单成功，解冻交易综合费" + totalFee + "元，返还积分：" + corder.getUsePoint() + "。订单ID：" + corder.getId()));
                                if (c3 != 1) throw new BussinessException("撤单成功，解冻交易综合费失败");
                            }
                            // 返还积分

                            String remark = "撤单成功，返回积分：" + corder.getUsePoint() + "。订单ID：" + corder.getUsePoint();
                            if (pointLogMapper.insert(PointLog.buildLog(userMapper.selectByPrimaryKey(corder.getUserId()), corder.getUsePoint(), PointNid.T8, remark)) != 1)
                                throw new BussinessException("返还用户积分，保存积分记录失败");
                            if (userMapper.updatePoint(corder.getUsePoint(), corder.getUserId()) != 1)
                                throw new BussinessException("返还用户积分失败");
                        } else {
                            int c3 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), totalFeeReal, AccountLog.TYPE.T16, account, "撤单成功，解冻交易综合费" + totalFeeReal + "元。订单ID：" + corder.getId()));
                            if (c3 != 1) throw new BussinessException("撤单成功，解冻交易综合费失败");
                        }
                        int c1 = accountMapper.updateAccount(BigDecimal.ZERO, totalFeeReal.add(corder.getDeposit()), totalFeeReal.add(corder.getDeposit()).negate(), account.getUserId());
                        if (c1 != 1) throw new BussinessException("期货撤单委托回报更新点买人资金账户失败");


                        // 处理投资人资金账户
                        int ic2 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), corder.getProfitDeposit(), AccountLog.TYPE.T22, ia, "点买人撤单成功，解冻止盈保证金" + corder.getProfitDeposit() + "元。订单ID：" + corder.getId()));
                        if (ic2 != 1) throw new BussinessException("点买人撤单成功，解冻止盈保证金失败");
                        int ic1 = accountMapper.updateAccount(BigDecimal.ZERO, corder.getProfitDeposit(), corder.getProfitDeposit().negate(), ia.getUserId());
                        if (ic1 != 1) throw new BussinessException("期货撤单委托回报更新点买人资金账户失败");

                    } else if (corder.getNumber() > num) {
                        // 部分撤单，先拆分订单
                        // 原订单号
                        String orderNo = corder.getOrderNo();

                        String orderNo1;
                        String orderNo2;
                        // 计算新订单相关参数 止盈、止损、履约保证金、止盈保证金（按数量比例分配）
                        BigDecimal scale = new BigDecimal(num).divide(new BigDecimal(corder.getNumber()), 8, BigDecimal.ROUND_HALF_UP);
                        BigDecimal deposit = scale.multiply(corder.getDeposit()).setScale(2, BigDecimal.ROUND_HALF_UP);
                        BigDecimal profitDeposit = scale.multiply(corder.getProfitDeposit()).setScale(2, BigDecimal.ROUND_HALF_UP);
                        // 拆分订单积分
                        Integer usePoint1 = 0;// 原订单积分数量
                        Integer usePoint2 = 0;// 新订单积分数量
                        if (corder.getUsePoint() > 0) {
                            usePoint2 = corder.getUsePoint() * model.getBusinessAmount() / corder.getNumber();
                            usePoint1 = corder.getUsePoint() - usePoint2;
                        }

                        // 处理订单号
                        if (orderNo.contains("C")) {
                            // 已拆分过
                            orderNo1 = orderNo;
                            orderNo2 = orderNo.substring(0, orderNo.indexOf("C")) + "C" + getLastPrefix(orderNo.substring(0, orderNo.indexOf("C")), "C");
                        } else {
                            // 未拆分过
                            orderNo1 = orderNo + "C1";
                            orderNo2 = orderNo + "C2";
                        }

                        // 更新原新单
                        Integer finalUsePoint1 = usePoint1;
                        if (orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                            t.setId(corder.getId());
                            t.setOrderNo(orderNo1);
                            t.setVersion(corder.getVersion());
                            t.setNumber(corder.getNumber() - num);
                            t.setDeposit(corder.getDeposit().subtract(deposit));
                            t.setProfitDeposit(corder.getProfitDeposit().subtract(profitDeposit));
                            t.setUsePoint(finalUsePoint1);
                        })) != 1) throw new BussinessException("更新原订单失败");

                        // 新增成交订单
                        Integer finalUsePoint2 = usePoint2;
                        orderMapper.insert(new Order(t -> {
                            BeanUtils.copyProperties(corder, t);
                            t.setId(null);
                            t.setVersion(null);
                            t.setOrderNo(orderNo2);
                            t.setNumber(num);
                            t.setStatus(Order.STATUS.T10.code);
                            t.setDeposit(deposit);
                            t.setProfitDeposit(profitDeposit);
                            t.setStatus(Order.STATUS.T10.code);
                            t.setUsePoint(finalUsePoint2);
                        }));
                        Order order2 = orderMapper.selectOne(new Order(o -> {
                            o.setOrderNo(orderNo2);
                            o.setDelFlag(Order.DELFLAG.NORMAL.code);
                        }));

                        // 订单总手续费
                        BigDecimal totalFee = order2.getFee().multiply(new BigDecimal(num)).setScale(2, BigDecimal.ROUND_HALF_UP);
                        // 实际扣除的手续费 = 订单手续费 - 积分抵扣
                        BigDecimal totalFeeReal = totalFee;
                        if (corder.getUsePoint() > 0 && totalFee.compareTo(new BigDecimal(order2.getUsePoint())) > 0) {
                            totalFeeReal = totalFee.subtract(new BigDecimal(order2.getUsePoint()));
                        } else if (order2.getUsePoint() > 0 && totalFee.compareTo(new BigDecimal(order2.getUsePoint())) <= 0) {
                            totalFeeReal = BigDecimal.ZERO;
                        }

                        // 处理资金账户
                        if (order2.getUsePoint() > 0) {
                            if (totalFeeReal.compareTo(BigDecimal.ZERO) > 0) {
                                int c3 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), totalFeeReal, AccountLog.TYPE.T16, account, "撤单成功，解冻交易综合费" + totalFee + "元，返还积分：" + order2.getUsePoint() + "。订单ID：" + order2.getId()));
                                if (c3 != 1) throw new BussinessException("撤单成功，解冻交易综合费失败");
                            }

                            String remark = "撤单成功，返回积分：" + order2.getUsePoint() + "。订单ID：" + order2.getUsePoint();
                            if (pointLogMapper.insert(PointLog.buildLog(userMapper.selectByPrimaryKey(order2.getUserId()), order2.getUsePoint(), PointNid.T8, remark)) != 1)
                                throw new BussinessException("返还用户积分，保存积分记录失败");
                            // 返还积分
                            if (userMapper.updatePoint(order2.getUsePoint(), order2.getUserId()) != 1)
                                throw new BussinessException("返还用户积分失败");
                        } else {
                            int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), order2.getDeposit(), AccountLog.TYPE.T19, account, "撤单成功，解冻履约保证金" + order2.getDeposit() + "元。订单ID：" + order2.getId()));
                            if (c2 != 1) throw new BussinessException("撤单成功，解冻履约保证金失败");
                        }

                        int c1 = accountMapper.updateAccount(BigDecimal.ZERO, totalFeeReal.add(order2.getDeposit()), totalFeeReal.add(order2.getDeposit()).negate(), account.getUserId());
                        if (c1 != 1) throw new BussinessException("期货撤单委托回报更新点买人资金账户失败");


                        // 处理投资人资金账户
                        int ic2 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), order2.getProfitDeposit(), AccountLog.TYPE.T22, account, "点买人撤单成功，解冻止盈保证金" + order2.getProfitDeposit() + "元。订单ID：" + order2.getId()));
                        if (ic2 != 1) throw new BussinessException("点买人撤单成功，解冻止盈保证金失败");
                        int ic1 = accountMapper.updateAccount(BigDecimal.ZERO, order2.getProfitDeposit(), order2.getProfitDeposit().negate(), ia.getUserId());
                        if (ic1 != 1) throw new BussinessException("期货撤单委托回报更新点买人资金账户失败");

                    } else {
                        // 撤单数量错误
                        throw new BussinessException("平仓数量有误");
                    }
                } else {
                    // 其他状态不处理
                }
                break;
            default:
                break;
        }
    }

    /**
     * 订单全部撤单
     *
     * @param order  订单
     * @param status 更新状态
     */
    private void doOrderCancle(Order order, Order.STATUS status, Order.CANCLETYPE cancleType) {
        // 更新订单状态
        int c = orderMapper.updateByIdAndVersionSelective(new Order(t -> {
            t.setId(order.getId());
            t.setVersion(order.getVersion());
            t.setStatus(status.code);
            t.setCancleTime(new Date());
            t.setCancleType(cancleType.code);
        }));
        if (c != 1)
            throw new BussinessException("更新订单状态失败，更新状态：" + status.code + "，原始状态：" + order.getStatus());


        // 手续费
        BigDecimal totalFee = order.getFee().multiply(new BigDecimal(order.getNumber())).setScale(2, BigDecimal.ROUND_HALF_UP);
        // 积分抵扣后手续费
        BigDecimal totalFeeReal = totalFee;
        if (order.getUsePoint() > 0 && totalFee.compareTo(new BigDecimal(order.getUsePoint())) > 0) {
            totalFeeReal = totalFee.subtract(new BigDecimal(order.getUsePoint()));
        } else if (order.getUsePoint() > 0 && totalFee.compareTo(new BigDecimal(order.getUsePoint())) <= 0) {
            totalFeeReal = BigDecimal.ZERO;
        }

        // 修改点买人账户金额（解冻履约保证金+解冻手续费），并记录资金流水
        BigDecimal totalAmount = order.getDeposit().add(totalFeeReal).setScale(2, BigDecimal.ROUND_HALF_UP);
        Account account = accountMapper.selectByUID(order.getUserId());
        if (totalFeeReal.compareTo(BigDecimal.ZERO) > 0) {
            int c2 = accountLogMapper.insert(AccountLog.buildLog(order.getUserId(), totalFeeReal, AccountLog.TYPE.T16, account, "撤单成功，解冻交易综合费：" + totalFeeReal + "元。订单ID：" + order.getId()));
            if (c2 != 1) throw new BussinessException("保存资金流水失败");
        }
        int c3 = accountLogMapper.insert(AccountLog.buildLog(order.getUserId(), order.getDeposit(), AccountLog.TYPE.T19, account, "撤单成功，解冻履约保证金：" + order.getDeposit() + "元。订单ID：" + order.getId()));
        if (c3 != 1) throw new BussinessException("保存资金流水失败");

        int c1 = accountMapper.updateAccount(BigDecimal.ZERO, totalAmount, totalAmount.negate(), order.getUserId());
        if (c1 != 1) throw new BussinessException("更新点买人账户资金失败");

        // 返还用户积分
        if (order.getUsePoint() > 0) {
            String remark = "撤单成功，返还使用积分：" + order.getUsePoint() + "。订单ID：" + order.getId();
            if (pointLogMapper.insert(PointLog.buildLog(userMapper.selectByPrimaryKey(order.getUserId()), order.getUsePoint(), PointNid.T8, remark)) != 1)
                throw new BussinessException("撤单记录用户积分退回记录失败");
            if (userMapper.updatePoint(order.getUsePoint(), order.getUserId()) != 1)
                throw new BussinessException("撤单返还用户积分失败");
        }

        // 解冻投资人止盈保证金
        if (order.getInvestId() != null && order.getInvestId() > 0) {
            Account ia = accountMapper.selectByUID(order.getInvestId());
            int c5 = accountLogMapper.insert(AccountLog.buildLog(order.getInvestId(), order.getProfitDeposit(), AccountLog.TYPE.T22, ia, "撤单成功，解冻止盈保证金：" + order.getProfitDeposit() + "元。订单ID：" + order.getId()));
            if (c5 != 1) throw new BussinessException("保存资金流水失败");
            int c4 = accountMapper.updateAccount(BigDecimal.ZERO, order.getProfitDeposit(), order.getProfitDeposit().negate(), order.getInvestId());
            if (c4 != 1) throw new BussinessException("更新投资人账户资金失败");
        }
    }

    /**
     * 查询同一订单最大尾号
     *
     * @param orderNo 原始订单号
     * @param regex   分割标志
     * @return
     */
    private synchronized int getLastPrefix(String orderNo, String regex) {
        List<String> list = orderMapper.selectOrderNos(orderNo);
        List<Integer> prefixs = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(list)) {
            for (String s : list) {
                if (s.contains(regex)) {
                    prefixs.add(Integer.parseInt(s.split(regex)[1]));
                }
            }
        }
        Collections.sort(prefixs);
        return prefixs.get(prefixs.size() - 1) + 1;
    }

    @Override
    public Integer doTrade(Integer userId, Integer commoId, Integer num, Integer lostPoint, Integer point, Integer openWay, Integer direction, BigDecimal entrustPrice, String resource, Integer follow, Integer followId, String ip) throws Exception {
        if (openWay == Order.OPENWAY.T2.code) {
            if (null == entrustPrice || entrustPrice.compareTo(BigDecimal.ZERO) != 1 || entrustPrice.toString().length() > 10 || !ValidateUtils.isPrice(entrustPrice.toString())) {
                throw new BussinessException("委托价格有误");
            }
        }
        final Commo commo = redisService.get(CacheID.COMMO_ID_PREFIX + commoId, Commo.class);
        if (commo == null) throw new BussinessException("交易品种不存在");
        if (commo.getStatus().intValue() == Commo.STATUS.T2.code) throw new BussinessException("交易品种已禁用");
//        Agent agent = agentMapper.selectOne(new Agent(t -> {
//            t.setDomain(resource);
//            t.setStatus(Agent.STATUS.T1.code);
//            t.setDelFlag(Agent.DELFLAG.NORMAL.code);
//        }));
        User user = userMapper.selectByPrimaryKey(userId);
        Agent agent = agentMapper.selectByPrimaryKey(user.getAgentId());
        if (agent == null) throw new BussinessException("查询代理商是失败");
        // 交易时间校验
        if (!riskParamService.isTradeTime(commoId)) throw new BussinessException("当前非交易时间，下单失败");
        // 暂停交易校验
        if (riskParamService.isTradePause(commoId)) throw new BussinessException("当前品种暂停交易，下单失败");
        // 查询用户资金账户
        final Account account = accountMapper.selectByUID(userId);
        // 下单参数校验
        String checkMsg = checkTrade(account, commoId, num, lostPoint, point, openWay, entrustPrice, direction, resource);
        if (StrUtil.isNotBlank(checkMsg)) throw new BussinessException(checkMsg);
        // 每点合约价格
        BigDecimal pointVlaue = riskParamService.getParamValue(RiskNID.POINT_VALUE, commoId, BigDecimal.class);
        // 止盈倍数
        BigDecimal profitStopTimes = riskParamService.getParamValue(RiskNID.PROFIT_STOP_TIMES, commoId, BigDecimal.class);
        // 对人民币汇率
        BigDecimal exchangeRate = riskParamService.getParamValue(RiskNID.EXCHANGE_RATE, commoId, BigDecimal.class);
        // 止损比例
        BigDecimal loseStopRate = riskParamService.getParamValue(RiskNID.LOSE_STOP_RATE, commoId, BigDecimal.class);
        // 市价单直接匹配投资人进行下单
        RealTimeData realTimeData = redisService.get(CacheID.QUOTE_REAL_PREFIX + commoId, RealTimeData.class);

        // 每手手续费
        BigDecimal fee;
        // 点买人盈利分成
        BigDecimal allotRate;
        if (follow.intValue() == Order.FOLLOW.T1.code) {
            // 点买策略
            fee = riskParamService.getFeeByAgent(commoId, agent.getId());
            allotRate = riskParamService.getParamValue(RiskNID.ALLOT_RATE, commoId, BigDecimal.class);
        } else {
            // 跟单策略
            fee = riskParamService.getParamValue(RiskNID.FOLLOW_FEE, commoId, BigDecimal.class).setScale(2, BigDecimal.ROUND_HALF_UP);
            allotRate = riskParamService.getParamValue(RiskNID.FOLLOW_ALLOT_RATE, commoId, BigDecimal.class);
        }

        // 止损金额 = 止损点数 * 每点合约价值 * 数量 * 汇率
        BigDecimal loseStop = pointVlaue.multiply(new BigDecimal(lostPoint * num)).multiply(exchangeRate).setScale(2, BigDecimal.ROUND_HALF_UP);
        // 履约保证金 = 止损金额 / 止损比例
        BigDecimal deposit = loseStop.divide(loseStopRate, 2, BigDecimal.ROUND_HALF_UP);
        // 止盈保证金 = 止损金额 * 止盈倍数
        BigDecimal profitDeposit = loseStop.multiply(profitStopTimes).setScale(2, BigDecimal.ROUND_HALF_UP);
        // 计算实际委托价格
        BigDecimal entrustPriceReal = getEntrustPrice(commo.getType(), openWay, direction, entrustPrice, realTimeData);

        // 止损下滑价格 = 止损点数 * 最小波动点
        BigDecimal lsp = new BigDecimal(lostPoint).multiply(riskParamService.getParamValue(RiskNID.WAVE_POINT_MIN, commoId, BigDecimal.class)).setScale(4, BigDecimal.ROUND_HALF_UP);
        BigDecimal loseStopMinPrice;
        BigDecimal profitStopMaxPrice;
        if (direction == Order.DIRECTION.T1.code) {
            // 最低止损价格 = 委托价 - 止损下滑价格
            loseStopMinPrice = entrustPriceReal.subtract(lsp).compareTo(BigDecimal.ZERO) != 1 ? BigDecimal.ZERO : entrustPriceReal.subtract(lsp);
            // 最高止盈价格 = 委托价 + （止损下滑价格 * 止盈倍数）
            profitStopMaxPrice = entrustPriceReal.add(lsp.multiply(profitStopTimes)).compareTo(BigDecimal.ZERO) != 1 ? BigDecimal.ZERO : entrustPriceReal.add(lsp.multiply(profitStopTimes));
        } else {
            // 最低止损价格 = 委托价 + 止损下滑价格
            loseStopMinPrice = entrustPriceReal.add(lsp).compareTo(BigDecimal.ZERO) != 1 ? BigDecimal.ZERO : entrustPriceReal.add(lsp);
            // 最高止盈价格 = 委托价 - （止损下滑价格 * 止盈倍数）
            profitStopMaxPrice = entrustPriceReal.subtract(lsp.multiply(profitStopTimes)).compareTo(BigDecimal.ZERO) != 1 ? BigDecimal.ZERO : entrustPriceReal.subtract(lsp.multiply(profitStopTimes));
        }
        // 手续费
        BigDecimal totalFee = fee.multiply(new BigDecimal(num)).setScale(2, BigDecimal.ROUND_HALF_UP);
        // 实际使用积分
        int realPoint = point;
        // 积分抵扣后手续费
        BigDecimal totalFeeReal = totalFee;
        if (point > 0 && totalFee.compareTo(new BigDecimal(point)) > 0) {
            totalFeeReal = totalFee.subtract(new BigDecimal(point));
        } else if (point > 0 && totalFee.compareTo(new BigDecimal(point)) <= 0) {
            totalFeeReal = BigDecimal.ZERO;
            realPoint = totalFee.intValue();
        }


        // 点买人下单所需总金额
        BigDecimal totalAmount = totalFeeReal.add(deposit).setScale(2, BigDecimal.ROUND_HALF_UP);
        // 账户余额校验
        if (account.getAvailable().compareTo(totalAmount) == -1) throw new BussinessException("账户余额不足，下单失败");


        // 保存策略信息
        BigDecimal finalLoseStopMinPrice = loseStopMinPrice;
        BigDecimal finalProfitStopMaxPrice = profitStopMaxPrice;
        BigDecimal finalEntrustPrice = entrustPriceReal;
        String orderNo = OrderNoUtils.getSerialNumber();
        int finalRealPoint = realPoint;
        Order order = new Order(t -> {
            t.setCommoId(commoId);
            t.setCode(commo.getCode());
            t.setName(commo.getName());
            t.setContract(riskParamService.getContractNo(commoId));
            t.setOrderNo(orderNo);
            t.setUserId(userId);
            t.setDirection(direction);
            t.setEntrustPrice(finalEntrustPrice);
            t.setOpenWay(openWay);
            t.setNumber(num);
            t.setLoseStopPoint(lostPoint);
            t.setLoseStopPrice(finalLoseStopMinPrice);
            t.setLoseStopMinPrice(finalLoseStopMinPrice);
            t.setProfitStopPrice(finalProfitStopMaxPrice);
            t.setProfitStopMaxPrice(finalProfitStopMaxPrice);
            t.setDeposit(deposit);
            t.setProfitDeposit(profitDeposit);
            t.setFee(fee);
            t.setAllotRate(allotRate);
            t.setResource(agent.getDomain());
            t.setAgentId(agent.getId());
            t.setStatus(Order.STATUS.T1.code);
            t.setFollow(follow);
            t.setFollowId(followId);
            t.setUsePoint(finalRealPoint);
//            t.setAutoSettleTime(riskParamService.getAutoSettleTime(commoId));
        });

        orderMapper.insert(order);

        order = orderMapper.selectOne(new Order(t -> t.setOrderNo(orderNo)));

        // 修改点买人账户金额（履约保证金+手续费），并记录资金流水
        if (realPoint > 0) {
            if (totalFeeReal.compareTo(BigDecimal.ZERO) > 0) {
                String remark = "下单成功，冻结交易综合费：" + totalFee + "元，积分抵扣：" + realPoint + "。订单ID：" + order.getId();
                int c2 = accountLogMapper.insert(AccountLog.buildLog(userId, totalFeeReal, AccountLog.TYPE.T15, account, remark));
                if (c2 != 1) throw new BussinessException("保存资金流水失败");
            }
            // 扣除用户积分
            if (pointLogMapper.insert(PointLog.buildLog(userMapper.selectByPrimaryKey(userId), -realPoint, PointNid.T7, "策略下单，积分抵扣扣除：" + realPoint + "。订单号：" + order.getId())) != 1) {
                throw new BussinessException("新增积分使用记录失败");
            }
            if (userMapper.updatePoint(-realPoint, userId) != 1) throw new BussinessException("扣除用户积分失败");
        } else {
            int c2 = accountLogMapper.insert(AccountLog.buildLog(userId, totalFeeReal, AccountLog.TYPE.T15, account, "下单成功，冻结交易综合费：" + totalFeeReal + "元。订单ID：" + order.getId()));
            if (c2 != 1) throw new BussinessException("保存资金流水失败");
        }
        int c3 = accountLogMapper.insert(AccountLog.buildLog(userId, deposit, AccountLog.TYPE.T18, account, "下单成功，冻结履约保证金：" + deposit + "元。订单ID：" + order.getId()));
        if (c3 != 1) throw new BussinessException("保存资金流水失败");
        int c1 = accountMapper.updateAccount(BigDecimal.ZERO, totalAmount.negate(), totalAmount, userId);
        if (c1 != 1) throw new BussinessException("更新点买人账户资金失败");

        try {
            // 匹配投资人
            doMatchInvestor(order);

            // 易盛下单
            if (Order.OPENWAY.T1.code == openWay.intValue()) {
                // 市价单 立即下单
                doTradeEntrust(order.getId(), ip);
            } else {
                if (direction == Order.DIRECTION.T1.code && entrustPrice.compareTo(realTimeData.getNp()) >= 0) {
                    // 限价买  并且委托价 >= 最新价  立即下单
                    doTradeEntrust(order.getId(), ip);
                } else if (direction == Order.DIRECTION.T2.code && entrustPrice.compareTo(realTimeData.getNp()) <= 0) {
                    // 限价卖  并且委托价 <= 最新价  立即下单
                    doTradeEntrust(order.getId(), ip);
                } else {
                    // 缓存限价单，限价单需要实时匹配行情价进行下单
                    String key = CacheID.LINIT_ORDER + order.getCommoId() + ":" + order.getDirection() + ":" + entrustPrice.setScale(4).toString();
                    redisService.mapPut(key, order.getId().toString(), order.getId().toString());
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
            // 匹配投资人失败不处理
        }
        return order.getId();
    }

    /**
     * 计算委托价格
     *
     * @param openWay      开仓方式 1市价  2限价
     * @param direction    开仓方向 1  2限价
     * @param entrustPrice 限价单委托价格
     * @param realTimeData 最新行情数据
     * @return
     */
    private BigDecimal getEntrustPrice(int commoType, int openWay, int direction, BigDecimal entrustPrice, RealTimeData realTimeData) {
        if (openWay == Order.OPENWAY.T1.code) {
            // 内盘市价单
            if (commoType == Commo.TYPE.T2.code) {
                if (Order.DIRECTION.T1.code == direction) {
                    // 买委托价 = 卖一价
                    return realTimeData.getAp1().setScale(4, BigDecimal.ROUND_HALF_UP);
                } else {
                    // 卖委托价 = 买一价
                    return realTimeData.getBp1().setScale(4, BigDecimal.ROUND_HALF_UP);
                }
            }

            // 外盘市价单
            switch (realTimeData.getCt()) {
                case "GC":
                    if (Order.DIRECTION.T1.code == direction) {
                        // 买委托价 = 卖一价
                        return realTimeData.getAp1().setScale(4, BigDecimal.ROUND_HALF_UP);
                    } else {
                        // 卖委托价 = 买一价
                        return realTimeData.getBp1().setScale(4, BigDecimal.ROUND_HALF_UP);
                    }
                case "CL":
                    if (Order.DIRECTION.T1.code == direction) {
                        // 买委托价 = 卖一价
                        return realTimeData.getAp1().setScale(4, BigDecimal.ROUND_HALF_UP);
                    } else {
                        // 卖委托价 = 买一价
                        return realTimeData.getBp1().setScale(4, BigDecimal.ROUND_HALF_UP);
                    }
                case "SI":
                    if (Order.DIRECTION.T1.code == direction) {
                        // 买委托价 = 卖一价
                        return realTimeData.getAp1().setScale(4, BigDecimal.ROUND_HALF_UP);
                    } else {
                        // 卖委托价 = 买一价
                        return realTimeData.getBp1().setScale(4, BigDecimal.ROUND_HALF_UP);
                    }
                case "HSI":
                    // 获取恒指委托偏移量
                    BigDecimal offset1 = redisService.get(ConfigNID.OFFSET_HSI, BigDecimal.class);
                    if (Order.DIRECTION.T1.code == direction) {
                        // 买委托价 = 卖一价 + 偏移量
                        return realTimeData.getAp1().add(offset1).setScale(4, BigDecimal.ROUND_HALF_UP);
                    } else {
                        // 卖委托价 = 买一价 - 偏移量
                        return realTimeData.getBp1().subtract(offset1).setScale(4, BigDecimal.ROUND_HALF_UP);
                    }
                case "MHI":
                    // 获取小恒指委托偏移量
                    BigDecimal offset2 = redisService.get(ConfigNID.OFFSET_MHI, BigDecimal.class);
                    if (Order.DIRECTION.T1.code == direction) {
                        // 买委托价 = 卖一价 + 偏移量
                        return realTimeData.getAp1().add(offset2).setScale(4, BigDecimal.ROUND_HALF_UP);
                    } else {
                        // 卖委托价 = 买一价 - 偏移量
                        return realTimeData.getBp1().subtract(offset2).setScale(4, BigDecimal.ROUND_HALF_UP);
                    }
                case "DAX":
                    // 获取德指委托偏移量
                    BigDecimal offset3 = redisService.get(ConfigNID.OFFSET_DAX, BigDecimal.class);
                    if (Order.DIRECTION.T1.code == direction) {
                        // 买委托价 = 卖一价 + 偏移量
                        return realTimeData.getAp1().add(offset3).setScale(4, BigDecimal.ROUND_HALF_UP);
                    } else {
                        // 卖委托价 = 买一价 - 偏移量
                        return realTimeData.getBp1().subtract(offset3).setScale(4, BigDecimal.ROUND_HALF_UP);
                    }
                default:
                    return BigDecimal.ZERO;
            }
        }
        return entrustPrice.setScale(4, BigDecimal.ROUND_HALF_UP);
    }


    /**
     * 匹配投资人
     *
     * @param order
     */
    private void doMatchInvestor(Order order) {
        Account ia = radomInvestor(order.getAgentId(), order.getProfitDeposit());
        if (ia == null) throw new BussinessException("订单匹配投资人失败");
        // 更新委托编号，投资人
        if (orderMapper.updateByIdAndVersionSelective(new Order(t -> {
            t.setId(order.getId());
            t.setVersion(order.getVersion());
            t.setStatus(Order.STATUS.T2.code);
            t.setInvestId(ia.getUserId());
        })) != 1) throw new BussinessException("订单匹配投资人失败");

        // 匹配投资人成功，处理投资人账户资金
        int ic2 = accountLogMapper.insert(AccountLog.buildLog(ia.getUserId(), order.getProfitDeposit(), AccountLog.TYPE.T21, ia, "匹配投资人成功，冻结止盈保证金：" + order.getProfitDeposit() + "元，订单ID：" + order.getId()));
        if (ic2 != 1) throw new BussinessException("保存资金流水失败");
        int ic1 = accountMapper.updateAccount(BigDecimal.ZERO, order.getProfitDeposit().negate(), order.getProfitDeposit(), ia.getUserId());
        if (ic1 != 1) throw new BussinessException("更新投资人账户资金失败");
    }

    /**
     * 调用易盛接口下单
     *
     * @param orderId
     * @param ip
     */
    @Override
    public synchronized void doTradeEntrust(Integer orderId, String ip) throws Exception {
        Order order = orderMapper.selectByPrimaryKey(orderId);
        if (order.getStatus() != Order.STATUS.T2.code) {
            throw new BussinessException("非委托中订单不能下单");
        }
        if (order.getInvestId() != null) {
            // 调用易盛下单接口，进行下单
            EntrustRes res = null;
            Commo commo = redisService.get(CacheID.COMMO_ID_PREFIX + order.getCommoId(), Commo.class);
            try {
                // 判断是国内期货还是国际期货
                if (commo.getType().intValue() == Commo.TYPE.T1.code) {
                    res = TransactionCall.getInstance().tradeEntrustOtc(buildOtcTradeParams(order, EntrustType.OPEN, ip));
                } else {
                    res = TransactionCall.getInstance().tradeEntrustNative(buildNativeTradeParams(order, EntrustType.OPEN, ip));
                }
                log.info("开仓下单结果：" + JSON.toJSONString(res));
            } catch (UFXLoginException e) {
                log.error("易盛登录异常：" + e.getMessage());
                e.printStackTrace();
                Investor investor = investorMapper.selectOne(new Investor(t -> t.setUserId(order.getInvestId())));
                // 重新登录
                LoginRes logres;
                if (commo.getType().intValue() == Commo.TYPE.T1.code) {
                    log.info("外盘账号登录：{}_{}", investor.getAccountNo(), investor.getAccountPwd());
                    logres = TransactionCall.getInstance().accountLogin(getOpStation(investor.getUserId(), "47.93.207.165"), investor.getAccountNo(), investor.getAccountPwd(), Commo.TYPE.T1.code);
                } else {
                    log.info("内盘账号登录：{}_{}", investor.getAccountHomeNo(), investor.getAccountHomePwd());
                    logres = TransactionCall.getInstance().accountLogin(getOpStation(investor.getUserId(), "47.93.207.165"), investor.getAccountHomeNo(), investor.getAccountHomePwd(), Commo.TYPE.T2.code);

                }
                log.info("易盛登录结果：" + JSON.toJSONString(logres));
                if (logres != null && StrUtil.isNotBlank(logres.getUserToken())) {
                    redisService.put(CacheID.INVESTOR + investor.getAccountNo(), logres.getUserToken(), 60 * 60 * 1);
                }
                throw new BussinessException("账号登录异常");
            }
            if (res == null || StrUtil.isBlank(res.getFutuEntrustNo())) throw new BussinessException("下单失败，接口无返回");

            // 更新委托编号，投资人
            EntrustRes finalRes = res;
            if (orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order.getId());
                t.setVersion(order.getVersion());
                t.setStatus(Order.STATUS.T2.code);
                t.setOpenEntrNo(finalRes.getFutuEntrustNo());
            })) != 1) throw new BussinessException("更新订单委托编号失败");
        }
    }

    @Override
    public synchronized void doLimitOrderTrade(Integer commoId, BigDecimal np) {
        log.info("实盘--行情驱动限价单下单，品种ID：{},最新价：{}", commoId, np);
        // 买涨限价策略 委托价 大于等于 最新价  挂单
        String upperKeys = CacheID.LINIT_ORDER + commoId + ":1";
        redisService.getKeys(upperKeys).stream().forEach(key -> {
            BigDecimal entrustPrice = new BigDecimal(key.substring(key.lastIndexOf(":") + 1));
            if (entrustPrice.compareTo(np) >= 0) {
                Map<String, String> map = redisService.map(key);
                if (map != null && map.size() > 0) {
                    map.entrySet().forEach(item -> {
                        try {
                            int orderId = Integer.parseInt(item.getValue());
                            redisService.mapDel(key, item.getKey().toString());
                            redisService.push(CacheID.QUEUE_LIMIT_ENTRUST, orderId);
                        } catch (Exception e) {
                            e.printStackTrace();
                            log.error("限价单行情驱动下单失败，错误消息：{}", e.getMessage());
                        }
                    });
                }
            }
        });
        // 买涨限价策略 委托价 大于等于 最新价  挂单
        String downKey = CacheID.LINIT_ORDER + commoId + ":2";
        redisService.getKeys(downKey).stream().forEach(key -> {
            BigDecimal entrustPrice = new BigDecimal(key.substring(key.lastIndexOf(":") + 1));
            if (entrustPrice.compareTo(np) <= 0) {
                Map<String, String> map = redisService.map(key);
                if (map != null && map.size() > 0) {
                    map.entrySet().forEach(item -> {
                        try {
                            int orderId = Integer.parseInt(item.getValue());
                            redisService.mapDel(key, item.getKey().toString());
                            redisService.push(CacheID.QUEUE_LIMIT_ENTRUST, orderId);
                        } catch (Exception e) {
                            e.printStackTrace();
                            log.error("限价单行情驱动下单失败，错误消息：{}", e.getMessage());
                        }
                    });
                }
            }
        });
    }

    @Override
    public synchronized void doCoverByLostProfit(BigDecimal np, String commoCode) {
        List<Order> list = orderMapper.selectByLostProfit(np, commoCode);
        List<Integer> ids = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(list)) {
            list.forEach(t -> ids.add(t.getId()));
        }
        log.info("实盘--触发止盈止损平仓,品种：{},最新价：{},处理订单IDS:{}", commoCode, np, JSON.toJSONString(ids));
        if (CollectionUtils.isNotEmpty(list)) {
            for (Order order : list) {
                try {
                    Map<String, Object> data = new HashMap<>();
                    data.put("orderId", order.getId());
                    data.put("num", order.getNumber());
                    if (order.getDirection().intValue() == Order.DIRECTION.T1.code) {
                        if (order.getLoseStopPrice().compareTo(np) != -1) {
                            // 触发止损平仓
                            data.put("settleType", Order.SETTLETYPE.T3.code);
                            redisService.push(CacheID.QUEUE_COVER_ORDER, data);
                        } else if (order.getProfitStopPrice().compareTo(np) != 1) {
                            // 触发止盈平仓
                            data.put("settleType", Order.SETTLETYPE.T2.code);
                            redisService.push(CacheID.QUEUE_COVER_ORDER, data);
                        }
                    } else {
                        if (order.getLoseStopPrice().compareTo(np) != 1) {
                            // 触发止损平仓
                            data.put("settleType", Order.SETTLETYPE.T3.code);
                            redisService.push(CacheID.QUEUE_COVER_ORDER, data);
                        } else if (order.getProfitStopPrice().compareTo(np) != -1) {
                            // 触发止盈平仓
                            data.put("settleType", Order.SETTLETYPE.T2.code);
                            redisService.push(CacheID.QUEUE_COVER_ORDER, data);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("止盈止损平仓操作失败，错误消息：" + e.getMessage());
                }
            }
        }
    }

    @Override
    public List<Order> selectByStatusUserId(Integer userId, Integer... status) {
        String str = "";
        for (int i = 0; i < status.length; i++) {
            str += status[i] + ",";
        }
        str = str.substring(0, str.length() - 1);
        Map<String, Object> params = new HashMap<>();
        params.put("statusSql", str);
        params.put("userId", userId);
        return orderMapper.selectByStatusUserId(params);
    }

    /**
     * 封装外盘下单参数
     *
     * @param order
     * @return
     */
    private EntrustOtcReq buildOtcTradeParams(Order order, String entrustType, String ip) throws Exception {
        // 查询投资人信息
        Investor investor = investorMapper.selectOne(new Investor(t -> t.setUserId(order.getInvestId())));
        // 系统标识
        String prefix = redisService.get(ConfigNID.SYS_FLAG) + "_";

        String token = redisService.get(CacheID.INVESTOR + investor.getAccountNo());
        if (StrUtil.isBlank(token)) {
            // 进行登录操作
            log.info("外盘账号登录：{}_{}", investor.getAccountNo(), investor.getAccountPwd());
            LoginRes res = TransactionCall.getInstance().accountLogin(getOpStation(investor.getUserId(), ip), investor.getAccountNo(), investor.getAccountPwd(), Commo.TYPE.T1.code);
            log.info("易盛登录结果：" + JSON.toJSONString(res));
            if (res == null || StrUtil.isBlank(res.getUserToken()))
                throw new BussinessException("易盛登录失败，返回信息：{}", JSON.toJSONString(res));
            token = res.getUserToken();
            redisService.put(CacheID.INVESTOR + investor.getAccountNo(), res.getUserToken(), 60 * 60 * 1);
        }

        String exchType = riskParamService.getParamValue(RiskNID.EXCH_TYPE, order.getCommoId());
        EntrustOtcReq params = new EntrustOtcReq();
        params.setUserToken(token);
        params.setOpStation(getOpStation(order.getUserId(), ip));
        params.setFundAccount(investor.getAccountNo());
        params.setFutuExchType(exchType);
        params.setContractCode(order.getContract());
        params.setEntrustBs(order.getDirection().toString());
        params.setEntrustAmount(order.getNumber());
        params.setEntrustReference(prefix + entrustType);
        params.setCommodityType(order.getCode());
        if ("HSI".equals(order.getCode()) || "MHI".equals(order.getCode())) {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date now = new Date();
            Date night = format.parse(format.format(now).substring(0, 10) + " 17:15:00");
            if (now.after(night)) {
                params.setHedgeType("1");
            }
            params.setTodayFlag("1");
            params.setFutuEntrustProp("0");
        } else {
            params.setTodayFlag("0");
            params.setFutuEntrustProp("1");
        }

        // 限价单下单处理委托价加点
        if (order.getOpenWay().intValue() == Order.OPENWAY.T2.code) {
            // 获取最新价
            RealTimeData realTimeData = redisService.get(CacheID.QUOTE_REAL_PREFIX + order.getCommoId(), RealTimeData.class);
            // 市价单
            switch (order.getCode()) {
                case "HSI":
                    // 获取恒指委托偏移量
                    BigDecimal offset1 = redisService.get(ConfigNID.OFFSET_HSI, BigDecimal.class);
                    if (Order.DIRECTION.T1.code == order.getDirection().intValue()) {
                        // 买委托价 = 卖一价 + 偏移量
                        params.setFutuEntrustPrice(realTimeData.getAp1().add(offset1).setScale(4, BigDecimal.ROUND_HALF_UP));
                    } else {
                        // 卖委托价 = 买一价 - 偏移量
                        params.setFutuEntrustPrice(realTimeData.getBp1().subtract(offset1).setScale(4, BigDecimal.ROUND_HALF_UP));
                    }
                    break;
                case "MHI":
                    // 获取小恒指委托偏移量
                    BigDecimal offset2 = redisService.get(ConfigNID.OFFSET_MHI, BigDecimal.class);
                    if (Order.DIRECTION.T1.code == order.getDirection().intValue()) {
                        // 买委托价 = 卖一价 + 偏移量
                        params.setFutuEntrustPrice(realTimeData.getAp1().add(offset2).setScale(4, BigDecimal.ROUND_HALF_UP));
                    } else {
                        // 卖委托价 = 买一价 - 偏移量
                        params.setFutuEntrustPrice(realTimeData.getBp1().subtract(offset2).setScale(4, BigDecimal.ROUND_HALF_UP));
                    }
                    break;
                case "DAX":
                    // 获取德指委托偏移量
                    BigDecimal offset3 = redisService.get(ConfigNID.OFFSET_DAX, BigDecimal.class);
                    if (Order.DIRECTION.T1.code == order.getDirection().intValue()) {
                        // 买委托价 = 卖一价 + 偏移量
                        params.setFutuEntrustPrice(realTimeData.getAp1().add(offset3).setScale(4, BigDecimal.ROUND_HALF_UP));
                    } else {
                        // 卖委托价 = 买一价 - 偏移量
                        params.setFutuEntrustPrice(realTimeData.getBp1().subtract(offset3).setScale(4, BigDecimal.ROUND_HALF_UP));
                    }
                    break;
                default:
                    // 其他品种不用加点
                    params.setFutuEntrustPrice(order.getEntrustPrice());
                    break;
            }
        } else {
            params.setFutuEntrustPrice(order.getEntrustPrice());
        }

        log.info("易盛下单参数：" + JSON.toJSONString(params));
        return params;
    }

    /**
     * 封装内盘下单参数
     *
     * @param order
     * @return
     */
    private EntrustNativeReq buildNativeTradeParams(Order order, String entrustType, String ip) throws Exception {
        // 查询投资人信息
        Investor investor = investorMapper.selectOne(new Investor(t -> t.setUserId(order.getInvestId())));
        // 系统标识
        String prefix = redisService.get(ConfigNID.SYS_FLAG) + "_";

        String token = redisService.get(CacheID.INVESTOR + investor.getAccountHomeNo());
        if (StrUtil.isBlank(token)) {
            // 进行登录操作
            log.info("内盘账号登录：{}_{}", investor.getAccountHomeNo(), investor.getAccountHomePwd());
            LoginRes res = TransactionCall.getInstance().accountLogin(getOpStation(investor.getUserId(), ip), investor.getAccountHomeNo(), investor.getAccountHomePwd(), Commo.TYPE.T2.code);
            log.info("易盛登录结果：" + JSON.toJSONString(res));
            if (res == null || StrUtil.isBlank(res.getUserToken())) {
                throw new BussinessException("内盘账号登录失败，返回信息：{}", JSON.toJSONString(res));
            }
            token = res.getUserToken();
            redisService.put(CacheID.INVESTOR + investor.getAccountHomeNo(), res.getUserToken(), 60 * 60 * 1);
        }

        String exchType = riskParamService.getParamValue(RiskNID.EXCH_TYPE, order.getCommoId());


        EntrustNativeReq params = new EntrustNativeReq();
        params.setUserToken(token);
        params.setFutuExchType(exchType);
        params.setContractCode(order.getCode() + order.getContract());
        params.setEntrustBs(order.getDirection().toString());

        if ("F3".equals(exchType)) {
            // 上期所不支持市价
            params.setEntrustProp("F0");

            // 获取最新价
            RealTimeData realTimeData = redisService.get(CacheID.QUOTE_REAL_PREFIX + order.getCommoId(), RealTimeData.class);

            BigDecimal wavePoint = riskParamService.getParamValue(RiskNID.WAVE_POINT_MIN, order.getCommoId(), BigDecimal.class);

            if (Order.DIRECTION.T1.code == order.getDirection().intValue()) {
                // 买委托价 = 卖一价 + 偏移量
                if (order.getOpenWay().intValue() == Order.OPENWAY.T1.code) {
                    params.setFutuEntrustPrice(realTimeData.getAp1().add(wavePoint).setScale(4, BigDecimal.ROUND_HALF_UP));
                } else {
                    params.setFutuEntrustPrice(order.getEntrustPrice().add(wavePoint).setScale(4, BigDecimal.ROUND_HALF_UP));
                }
            } else {
                // 卖委托价 = 买一价 - 偏移量
                if (order.getOpenWay().intValue() == Order.OPENWAY.T1.code) {
                    params.setFutuEntrustPrice(realTimeData.getBp1().subtract(wavePoint).setScale(4, BigDecimal.ROUND_HALF_UP));
                } else {
                    params.setFutuEntrustPrice(order.getEntrustPrice().subtract(wavePoint).setScale(4, BigDecimal.ROUND_HALF_UP));
                }
            }

        } else {
            // 非上期所，支持市价
            params.setEntrustProp("F1");
            params.setFutuEntrustPrice(BigDecimal.ZERO);
        }

        if (EntrustType.OPEN.equals(entrustType)) {
            // 开仓
            params.setFuturesDirection("1");
        } else {
            // 平仓  2平昨仓  4平今仓
            params.setFuturesDirection("4");
        }

        params.setEntrustAmount(order.getNumber().intValue());
        params.setEntrustAmount(order.getNumber());
        params.setEntrustReference(prefix + entrustType);

        log.info("内盘下单参数：" + JSON.toJSONString(params));
        return params;
    }

    /**
     * 获取站点信息
     *
     * @param userId
     * @param ip
     * @return
     */
    private String getOpStation(Integer userId, String ip) {
        User user = userMapper.selectByPrimaryKey(userId);
        return "MPN:" + user.getMobile() + ";" + "IP:" + ip;
    }

    /**
     * 后台已知投资人，接单
     *
     * @param order
     */
    private void handTradeEntrust(Order order, Investor investor) {
        Account investorAccount = accountMapper.selectByPrimaryKey(investor.getUserId());
        if (null == investorAccount) {
            throw new BussinessException("投资人平台账户不存在");
        }
        //校验投资人账户余额
        if (BigDecimalUtils.isLessThan(investorAccount.getAvailable(), order.getProfitDeposit()))
            throw new BussinessException("投资人账户余额不足");
        try {
            // 修改投资人账户金额，并记录资金流水
            int ic2 = accountLogMapper.insert(AccountLog.buildLog(investor.getUserId(), order.getProfitDeposit(), AccountLog.TYPE.T21, investorAccount, "投资人接单成功，冻结止盈保证金：" + order.getProfitDeposit() + "元，订单ID：" + order.getId()));
            if (ic2 != 1) throw new BussinessException("保存资金流水失败");
            int ic1 = accountMapper.updateAccount(BigDecimal.ZERO, order.getProfitDeposit().negate(), order.getProfitDeposit(), investor.getUserId());
            if (ic1 != 1) throw new BussinessException("更新投资人账户资金 失败");

            // 扣除点买人冻结的手续费
            Account account = accountMapper.selectByUID(order.getUserId());
            BigDecimal fee = order.getFee().multiply(new BigDecimal(order.getNumber())).setScale(2, BigDecimal.ROUND_HALF_UP);
            int c2 = accountLogMapper.insert(AccountLog.buildLog(account.getUserId(), fee, AccountLog.TYPE.T17, account, "扣除交易综合费" + fee + "元。订单ID：" + order.getId()));
            if (c2 != 1) throw new BussinessException("期货成交回报扣除交易综合费失败资金记录保存失败");
            int c1 = accountMapper.updateAccount(fee.negate(), BigDecimal.ZERO, fee.negate(), account.getUserId());
            if (c1 != 1) throw new BussinessException("期货成交回报扣除交易综合费失败");

            // 更新委托编号，投资人
            orderMapper.updateByIdAndVersionSelective(new Order(t -> {
                t.setId(order.getId());
                t.setVersion(order.getVersion());
                t.setInvestId(investor.getUserId());
                t.setStatus(Order.STATUS.T3.code);
                t.setOpenPrice(redisService.get(CacheID.QUOTE_REAL_PREFIX + order.getCommoId(), BigDecimal.class));
            }));
        } catch (Exception e) {
            e.printStackTrace();
            throw new BussinessException(e.getMessage());
        }
    }

    /**
     * 随机分配符合条件的投资人（账户可用金额大于等于止盈保证金）
     *
     * @param agentId       代理商Id
     * @param profitDeposit 止盈保证金
     * @return
     */
    private Account radomInvestor(Integer agentId, BigDecimal profitDeposit) {
        List<Account> list = accountMapper.selectInvestAccount(agentId, profitDeposit);
        if (CollectionUtils.isNotEmpty(list)) {
            return list.get(RandomUtil.randomInt(list.size()));
        }
        return null;
    }


    /**
     * 下单校验
     *
     * @param account
     * @param commoId
     * @param num
     * @param lostPoint
     * @param point
     * @param openWay
     * @param entrustPrice
     * @param direction
     * @param resource     @return
     */
    private String checkTrade(Account account, Integer commoId, Integer num, Integer lostPoint, Integer point, Integer openWay, BigDecimal entrustPrice, Integer direction, String resource) {

        if (StrUtil.isBlank(resource)) return "订单来源不正确";

        if (account == null) return "查询用户账户信息失败";

        if (commoId == null || commoId <= 0) return "交易品种不正确";

        if (num == null || num.intValue() <= 0) return "交易数量不正确";

        if (!Order.OPENWAY.getEnumMap().containsKey(openWay)) return "开仓类型不正确";

        if (!Order.DIRECTION.getEnumMap().containsKey(direction)) return "交易方向不正确";

        // 止损点数列表
        List<Integer> lostStopPoints = riskParamService.getParam2IntList(RiskNID.LOSE_STOP_POINT, commoId);
        if (!lostStopPoints.contains(lostPoint)) return "止损金额选择错误";

        if (openWay.intValue() == Order.OPENWAY.T2.code && (entrustPrice == null || entrustPrice.compareTo(BigDecimal.ZERO) != 1))
            return "委托价格不正确";

        // 校验积分是否充足
        User user = userMapper.selectByPrimaryKey(account.getUserId());
        if (point < 0 || point > user.getPoint()) throw new BussinessException("使用积分数量不正确");
        if (user.getPoint() < point) throw new BussinessException("积分余额不足");

        return null;

    }


    public List<OrderModel> userSum(String startTime, String endTime, Integer agentId) {
        Map<String, Object> params = new HashMap<>();
        List<Commo> commos = commoMapper.select(new Commo(commo -> {
            commo.setStatus(Commo.DELFLAG.NORMAL.code);
        }));
        String timeSql = "";
        if (StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime)) {
            timeSql = " AND o.create_time <= '" + endTime + "' AND o.create_time >= '" + startTime + "' ";
        }
        String agentSql = "";
        if (StringUtils.isNotBlank(agentId)) {
            agentSql = " AND o.agent_id = " + agentId;
        }
        List<OrderModel> list = new ArrayList<>();
        if (commos != null && commos.size() > 0) {
            for (Commo co : commos) {
                OrderModel model = new OrderModel();
                String followSql = " AND o.follow = " + Order.FOLLOW.T1.code;
                ;
                model.setCode(co.getCode());
                //累计点买数量
                String whereSql = " AND o.`status` = " + Order.STATUS.T6.code + " AND o.`code` = '" + co.getCode() + "' AND o.`del_flag` = 1 " + followSql + timeSql + agentSql;
                params.put("whereSql", whereSql);
                if (orderMapper.findNumSum(params) == null) {
                    model.setSumBuyNum(0);
                } else {
                    model.setSumBuyNum(orderMapper.findNumSum(params));
                }
                //累计点买盈亏
                if (orderMapper.findProfitSum(params) == null) {
                    model.setSumBuyProfit(BigDecimal.ZERO);
                } else {
                    model.setSumBuyProfit(orderMapper.findProfitSum(params));
                }

                params.remove("whereSql");
                followSql = " AND o.follow = " + Order.FOLLOW.T2.code;
                whereSql = " AND o.`status` = " + Order.STATUS.T6.code + " AND o.`code` = '" + co.getCode() + "' AND o.`del_flag` = 1 " + followSql + timeSql + agentSql;
                params.put("whereSql", whereSql);
                //累计跟单数量
                if (orderMapper.findNumSum(params) == null) {
                    model.setSumFollowNum(0);
                } else {
                    model.setSumFollowNum(orderMapper.findNumSum(params));
                }
                //累计跟单盈亏
                if (orderMapper.findProfitSum(params) == null) {
                    model.setSumFollowProfit(BigDecimal.ZERO);
                } else {
                    model.setSumFollowProfit(orderMapper.findProfitSum(params));
                }
                params.remove("whereSql");
                whereSql = " AND o.`status` = " + Order.STATUS.T6.code + " AND o.`code` = '" + co.getCode() + "' AND o.`del_flag` = 1 " + timeSql + agentSql;
                params.put("whereSql", whereSql);

                model.setSumUser(orderMapper.findUserSum(params));

                if (orderMapper.findPenetrateSum(params) == null) {
                    model.setSumPenetrate(BigDecimal.ZERO);
                } else {
                    model.setSumPenetrate(orderMapper.findPenetrateSum(params));
                }
                if (orderMapper.findFeeSum(params) == null) {
                    model.setSumFee(BigDecimal.ZERO);
                } else {
                    model.setSumFee(orderMapper.findFeeSum(params));
                }
                if (orderMapper.findDividedSum(params) == null) {
                    model.setSumDivided(BigDecimal.ZERO);
                } else {
                    model.setSumDivided(orderMapper.findDividedSum(params));
                }
                list.add(model);
            }
        }
        return list;
    }

}
